I'm trying to, at compile time, select a type to use depending on whether one is publicly available in a given scope. It's best to go straight to the code:
#include <iostream>
#include <type_traits>
class Logger
{
std::string _p;
public:
Logger(std::string p): _p(p)
{ }
void say(std::string message)
{ std::cout << _p << ' ' << message << std::endl; }
};
struct Log
{
static Logger& log()
{
static Logger _def("Default: ");
return _def;
}
};
// 1.
template <typename P>
struct use_logger
{
static std::size_t test(P*);
static char test(...);
static const bool value = sizeof(test(reinterpret_cast<P*>(0))) == sizeof(std::size_t);
};
class A
{
struct Log
{
static Logger& log()
{
static Logger _def("A: ");
return _def;
}
};
public:
void say()
{
std::cout << "A: " << use_logger<Log>::value << std::endl;
std::conditional<use_logger<Log>::value, Log, ::Log>::type::log().say("From A");
}
};
class B
{
public:
void say()
{
std::cout << "B: " << use_logger<Log>::value << std::endl;
std::conditional<use_logger<Log>::value, Log, ::Log>::type::log().say("From B");
}
};
class C : A
{
public:
void say()
{
std::cout << "C: " << use_logger<Log>::value << std::endl;
//2.
std::conditional<use_logger<Log>::value, Log, ::Log>::type::log().say("From C");
// Log::log().say("From C");
}
};
class D : public A
{
public:
void say()
{
// 2.
std::cout << "D: " << use_logger<Log>::value << std::endl;
std::conditional<use_logger<Log>::value, Log, ::Log>::type::log().say("From D");
// Log::log().say("From C");
}
};
int main(void)
{
{
A i;
i.say();
}
{
B i;
i.say();
}
{
C i;
i.say();
}
{
D i;
i.say();
}
}
My intention is that in A
, there is a type Log
, so that should be used rather than the global ::Log
, and in B
where there is none, it should use the global ::Log
. Now both these work irrespective of 1.
(my incorrect test to see if the type is private in this scope..)
The problem is in C
and D
, normally - without the test, Log::log()
fails, because it's private in A
. However if the std::conditional<>
is used, there is no compilation error, and the output is incorrect as it is prefixed with A:
. So, what have I missed (apart from the incorrect test - which I need to somehow fix...)? If nothing, then is this approach of exposing the private type in A
using the std::conditional
legal?
EDIT: for sanity, I tested with the following:
std::conditional<false, Log, ::Log>::type::log("From C");
std::conditional<false, Log, ::Log>::type::log("From D");
And it does use the global ::Log
, if it's true, it's somehow using the private A::Log
.
EDIT2: Infact this appears to be a more general condition, i.e. you can easily get access to some internal private types via a template indirection, e.g:
class F
{
struct Foo
{
void bar() { }
};
};
template <typename T>
struct ExposeInternal
{
typedef T type;
};
int main(void)
{
{
// We've got Foo!
ExposeInternal<F::Foo>::type t;
t.bar();
}
{
// Below fails
F::Foo t;
t.bar();
}
}
EDIT 3: Okay - have confirmed, it is a reported GCC bug, nothing to do with std::conditional
, not yet fixed in 4.7 or 4.8. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47346
I will leave this question open for the moment.. will close it with the above later.
#include <string>
and a semicolon after a struct. That's not an SSCCE. – Nils;
, else it compiles fine. ideone.com/9YJWIu The unexpected was that, 1. it compiles and 2. the output is prefixed with "A: " both of which I mention already in the question... I would have expected a compiler error, as you get if you try to use the type directly... – Dugald<string>
, it's usingstd::string
. The fact that either<iostream>
or<type_traits>
pulls in<string>
on your implementation does not imply that it does on every implementation. – Herbartstd::conditional
to get at a type I have no business accessing. – Junie