Is it possible to check if a member variable, a member function, or a type definition is declared in a given class?
Various questions on StackOverflow talk about checking if a given class merely contains a member, essentially using std::is_detected. But all these solutions detect the member also in derived classes, which may not declare the member themselves.
For example, the following doesn't compile.
#include <experimental/type_traits>
struct base
{
using type = std::true_type;
};
struct derived : public base { };
template<typename T>
using has_type_t = typename T::type;
template<typename T>
constexpr inline bool has_type_v =
std::experimental::is_detected<has_type_t, T>::value;
int main ()
{
static_assert (has_type_v<base>);
static_assert (!has_type_v<derived>);
}
Can any changes be made so that the two assertions hold? Or is reflection needed for that?
I don't see a way for type or static member, but for regular member, you can have distinguish base::m from derived::m:
template<typename T>
using has_m_t = decltype(&T::m);
template<typename T>
constexpr inline bool has_m_v =
std::experimental::is_detected_exact<int (T::*), has_m_t, T>::value;
And the test:
struct base { int m; };
struct derived : public base {};
struct without {};
static_assert( has_m_v<base>);
static_assert(!has_m_v<derived>);
static_assert(!has_m_v<without>);
Demo
I'm inclined to say no. Proving that is of course hard, but I can explain why I think so.
C++ is a compiled language. The compiler has an internal representation of types, which you can't access directly. The only way to access this internal representation is through the facilities of the language. Actual implementations can vary in the way they represent types internally, and often do have additional information to produce better error messages. But this is not exposed.
So yes, most compilers can enumerate base types, and know exactly where each member of a class came from. That's essential for good error messages. But you can't enumerate base classes at compile time, using only C++ templates.
You might think that for data members you could try tricks with addresses and offsets. That won't work, again because you need to know the size of all base classes - and you can't enumerate those.
You might also consider tricks in which you create further-derived helper classes. That's possible, but they suffer from the same problem. You can only derive from the given type, not from its parent(s). It's thus possible to create a child class, but not a sibling. And that child inherits from parent and grandparent alike. Even if you wrote using derived::type in the helper class, that would find base::type.
You can but with a limitation, your compiler must have an intrinsic that provides the list of base classes (GCC provides it).
If you can have access to such en intrinsic, the only difficulty is to check that member access through the derived is not actualy an access to a member of the base.
To check this, the idea is to use the 'ambiguous' access that happens when accessing a member declared in multiple bases of a derived class:
struct base
{
using type = std::true_type;
};
struct Derived1 : base{
};
struct Derived2 : base{
using type = std::true_type;
};
struct Test1: Derived1,base{};
struct Test2: Derived2,base{};
void g(){
Test1::type a;
Test2::type b;//Do not compile: 'type' is ambiguous
}
So you can generalize this trick this way:
template<class T,class Base>
struct MultiDer: T,Base{};
template<class T,class Base,class=void>
struct has_ambiguous_type
:std::true_type{};
template<class T,class Base>
struct has_ambiguous_type
<T,Base,std::void_t<typename MultiDer<T,Base>::type>>
:std::false_type{};
template<class T,class=void>
struct has_type
:std::false_type{};
template<class T>
struct has_type
<T,std::void_t<typename T::type>>
:std::true_type{};
template<class T,class...Bases>
constexpr inline auto has_type_declared_imp =
has_type<T>::value
//if any ambiguous access happens then T has 'type'
&& ( (has_ambiguous_type<T,Bases>::value || ...)
//or no ambiguity happened because none of the base has 'type'
|| (!has_type<Bases>::value && ...));
template<class T>
constexpr inline auto has_type_declared =
has_type_declared_imp<T,__direct_bases(T)...>;//GCC instrinsic
static_assert(has_type_declared<Derived2>);
static_assert(!has_type_declared<Derived1>);
The only problem is portability: your compiler must provides a mechanism to get access to the list of direct bases of a type. Here I have used the GCC's intrinsic __direct_bases.
Related
I have a template and many derived classes. In all of these classes I want to use the template type and some dependent types of it (actually I have many types and many dependent types).
The following approach is quite bad, since I need to manually inherit (with using keyword) all the types from the parent class (see code example):
EDIT: I didn't know that it is important that the class where I want to use the types is also a template. I updated the code example accordingly.
template<class T>
class A {
using TBase = T;
using TDerived = T[10];
}
template<class T>
class B : A<T> {
// NOTE i want the types TBase and TDerived here and in further children
using TBase = A<T>::TBase;
using TDerived = A<T>::TDerived
}
class C : B<int> {
// NOTE i want the types TBase and TDerived here and in further children
using TBase = B<int>::TBase;
using TDerived = B<int>::TDerived
}
I also thought about templated namespaces so that I could define the types outside of the class - but they don't exist.
Does anyone have a better idea?
SOLUTION1: fix current approach
(based on the comments from Jan Hudec and the answer from Oguk)
public inheritance
I forget the keyword public: for classes (struct is public by default).
missing typename
The compiler warning for my (wrong) types where used in the class where misleading (although the ones for the using keyword directly hint to the missing typename keyword).
Further notes about when to use typename can be found here: https://stackoverflow.com/a/7923419/3779655
type interitance
When I correctly specify the type (e.g. see 2. missing typename) then I can also use the current class name.
Resulting code:
template<class T>
class A {
public:
using TBase = T;
using TDerived = T[10];
};
template<class T>
class B : public A<T> {
public:
// possiblity 1:
using TBase = typename B::TBase;
TBase memberB1;
// possibility 2
typename B::TBase memberB2;
};
class C : public B<int> {
public:
// NOTE C is no template -> we don't have to do anything
TBase memberC;
};
SOLUTION2: trait classes
(based on the answer from Oguk)
use templated trait class instead of namespace
This gives pretty much the same functionality.
different availability of type information
One thing I didn't think of before is to check which type information is available where and if further characters are needed to retrieve the needed type information from a parent class (see NOTE comments).
See code below:
template<class T>
struct TypesContainer {
using TBase = T;
using TDerived = T[10];
};
template<class T>
class A {
public:
// TODO possible to directly inherit all types?
//using TypesContainer<T>;
// NOTE this is only possible for methods, not types
using Types = TypesContainer<T>;
typename Types::TBase memberA;
};
class B : public A<int> {
public:
// NOTE here I have all template information via definition (by hand)
using Types = TypesContainer<int>;
typename Types::TBase memberB;
};
class C : public B {
public:
// NOTE here I don't have the type information any more
using Types = TypesContainer<A::Types::TBase>;
typename Types::TBase memberC;
};
PROBLEM:
Is there a way to directly inherit all members of a namespace/class within the class scope (see TODO comment). Using the using keyword without assignement operator = does only work for methods not namespaces, classes and types.
First, if you are otherwise fine with your design, it should work if you inherit the base class publicly. This would work in the code you posted. However, if your example is very simplified, there might be another reason you cannot access the typedefs in your real code: The thing which could cause problems for you is if your derived class is actually also a templated class with the type parameter T, e.g. B<T>. This would make the base class A<T> dependent and its members would not considered during lookup if you use a non-dependent name (i.e. only TBase) to refer to them inside B<T>, so they would not be found. Referring to them as typename A<T>::TBase would solve that problem by making the name dependent names.
If you want to follow your other idea and have that type information available outside of the class (because it is conceptually more connected to T and not so much to the class A), instead of the "templated namespaces" you were thinking of, usually, trait classes are used for this purpose:
template <class T>
struct MyTraits {
using TBase = T;
using TDerived = T[10];
}
Then you can access those using declarations (or, alternatively, typedefs) from anywhere by using MyTraits<T>::TBase. This is effectively what you would achieve with a templated namespace. Don't forget to add the typename keyword if you use the using/typedef members of MyTraits in a dependent context, i.e. typename MyTraits<T>::TBase.
I am trying to understand the following code. Derived is a derived structure from T and what does "," means and then Fallback {}
template <class T>
struct has_FlowTraits<T, true>
{
struct Fallback { bool flow; };
struct Derived : T, Fallback { }; //What does it means ?
template<typename C>
static char (&f(SameType<bool Fallback::*, &C::flow>*))[1];
template<typename C>
static char (&f(...))[2];
public:
static bool const value = sizeof(f<Derived>(0)) == 2;
};
It's an implementation of Member Detector Idiom. It uses SFINAE to check whether type T has got a member called flow.
Edit: The comma part you're asking about is multiple inheritance. Struct Derived is (publicly) inheriting from both T and Fallback.
It's just multiple inheritance. The following is a Derived that is derived from T (and provides no further definition):
struct Derived : T { };
And the following is a Derived that is derived from both T and Fallback:
struct Derived : T, Fallback { };
That is, Derived will inherit the members of T and the members of Fallback. In this case, since Derived is a struct, the inheritance is by default public inheritence.
It means:
inside the definition of has_FlowTraits struct, you also define a new struct which is called Derived.
You say that this struct Derived is inheriting the type T, and the type Fallback. ( If you look at the line before, the struct Fallback has just been defined).
the {} simply means that there are no more details of implementation. No more method or attribute definition is needed for this type to be useful.
The comma means it derives either publicly or privately (depending on whether the T or Fallback is a struct or class) from those two classes. The comma simply includes those classes as those from which Derive will derive.
Why does the following not compile?
template <typename Child> struct Base
{
typename Child::Type t; // Does not compile. "No type named Type in Child"
};
struct Derived : public Base<Derived>
{
typedef int Type;
};
How is that Base cannot access its Child's Type? I tried the same with a static function instead of a typedef, and that works just fine.
I tried both GCC 4.4.2 and clang 3.0.
That kind of code won't work because Derived is not fully defined yet at the point in which Base is instantiated. It'll basically be an incomplete type.
Alternatives can range from simple to very complex. Probably the simplest way, if you can do it, is to avoid working with Child::Type until you actually need it (lazy evaluation, essentially). It would help if you state exactly what you want to achieve.
In completement to stinky472 answer, if you base is dependant on having Type, then you could do a lot worse than
template<typename Child, typename Type>
struct base
{
Type t;
};
struct Derived : public Base<Derived, int>
{
};
It's not as clean though.
In this piece I'm trying to declare in Class B a list that can hold objects of Class A of any type, such as A<int>, A<double>, A<float>. I intend to add A objects to the list during runtime:
#include <list>
template <class T> class A {};
class B {
template<class T> std::list<A<T>*> objects;
};
It seems like making a list like this should work but compiling it gives an error:
Line 6: error: data member 'objects' cannot be a member template
compilation terminated due to -Wfatal-errors.
Can somebody explain why this doesn't work and how I can fix it?
That's just not how C++ works. If you want to group different objects together, they need to have at least some relation. Being instantiations of the same class template doesn't imply that they are related, they're completely distinct types. If you want a list of A<T>*s, better make a list of base-class pointers and forward operations through virtual functions:
class A_base{
public:
virtual void foo() = 0;
virtual ~A_base() { }
};
template<class T>
class A : public A_base{
public:
void foo(){
// ...
}
};
class B{
std::list<A_base*> objects;
};
Member variables aren't allowed to be templates. Only member functions can be templates. You'll have to templatize the enclosing class B instead:
template <class T>
class B {
std::list<A<T>*> objects;
};
Unfortunately you cannot have template variables. Only option to declare a member data is to make the class template:
template<class T>
class B {
std::list<A<T>*> objects;
};
Depending on what you're doing, type erasure might be an option. On the Tension Between Object-Oriented and Generic Programming in C++ is my favorite write-up on the subject.
In a nutshell, you convert the static dispatch enabled by the templates into dynamic dispatch through a custom inheritance tree you setup on the fly. Instead of storing A<T>, you create a new type that has the common interface you desire, and using some template/inhertiance voodoo this new type stores an A<T> without actually exposing the T. So A<int> and A<double> and A<A<std::list<A<int> > > > and some_type_that_looks_like_A_but_really_isnt all reduce down to a single type.
But you have to have a common interface, independant of that parameter. If you can't, things get more difficult.
Boost.Any is a good example, as is std::shared_ptr [which uses type erasure to remember how to delete the pointer passed to it even in the face of non-polymorphic inheritance].
Make B class template just like you've made A a class template:
template<class T>
class B {
std::list<A<T>*> objects;
};
I have my templated container class that looks like this:
template<
class KeyType,
class ValueType,
class KeyCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<KeyType>,
class ValueCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<ValueType>
>
class MyClass
{
[...]
}
Which means that when I instantiate an object of this class, I can do it several different ways:
MyClass<MyKeyType, MyValueType> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor, MyCustomValueCompareFunctor> myObject;
Those are all good. The problem comes when I want to instantiate a MyClass that uses a non-default version of the ValueCompareFunctor argument, but I still want to use the default value of the KeyCompareFunctor argument. Then I have to write this:
MyClass<MyKeyType, MyValueType, AnObnoxiouslyLongSequenceOfCharacters<MyKeyType>, MyCustomValueCompareFunctor> myObject;
It would be much more convenient if I could somehow omit the third argument and just write this:
MyClass<KeyType, ValueType, MyCustomValueCompareFunctor> myObject;
Since the MyCustomValueCompareFunctor works only on objects of type MyValueType and not on objects of type MyKeyType, it seems like the compiler could at least theoretically work out what I meant here.
Is there a way to do this in C++?
In general, both in templates and functions or methods, C++ lets you use default for (and thereby omit) only trailing parameters -- no way out.
I recommend a template or macro to shorten AnObnoxiouslyLongSequenceOfCharacters<MyKeyType> to Foo<MyKeyType> -- not perfect, but better than nothing.
No. The closest you can come is to allow users to specify some sentinel type - like void - meaning "use default value here", and use template metamagic inside your class to typedef the real default if void was given to you. But this probably isn't a good idea from readability point of view.
Boost parameters and Boost graph named parameters are efforts towards naming parameters for template functions/methods. They give the opportunity to provide arguments in whichever order you prefer. Some arguments may be optional, with default values.
The same approach may be applied to template arguments. Instead of having N template arguments + P optional ones, create your class with N+1 template arguments. The last one will hold "named" parameters which can be omitted.
This answer is not complete yet, but i hope it's a good start !
An alternative option is to use Traits classes:
template <class KeyType>
class KeyTraits
{
typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType> Compare;
};
template <class ValueType>
class ValueTraits
{
typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType> Compare;
};
template<class KeyType class ValueType>
class MyClass
{
typedef KeyTraits<KeyType>::Compare KeyCompareFunctor;
typedef ValueTraits<ValueType>::Compare KeyCompareFunctor;
};
Then if you have a type which needs a different comparison function for Key's, then you'd explicitly specialize the KeyTraits type for that case. Here's an example where we change it for int:
template <>
class KeyTraits<int>
{
typedef SpecialCompareForInt Cmopare;
};
There is another option, which uses inheritance and which works like the following. For the last two arguments, it uses a class that inherits virtually from a class that has two member templates, that can be used to generate the needed types. Because the inheritance is virtual, the typedefs it declares are shared among the inheritance as seen below.
template<class KeyType,
class ValueType,
class Pol1 = DefaultArgument,
class Pol2 = DefaultArgument>
class MyClass {
typedef use_policies<Pol1, Pol2> policies;
typedef KeyType key_type;
typedef ValueType value_type;
typedef typename policies::
template apply_key_compare<KeyType>::type
key_compare;
typedef typename policies::
template apply_value_compare<ValueType>::type
value_compare;
};
Now, have a default argument that you use, which has typedefs for the default arguments you want provide. The member templates will be parameterized by the key and value types
struct VirtualRoot {
template<typename KeyType>
struct apply_key_compare {
typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType>
type;
};
template<typename ValueType>
struct apply_value_compare {
typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType>
type;
};
};
struct DefaultArgument : virtual VirtualRoot { };
template<typename T> struct KeyCompareIs : virtual VirtualRoot {
template<typename KeyType>
struct apply_key_compare {
typedef T type;
};
};
template<typename T> struct ValueCompareIs : virtual VirtualRoot {
template<typename ValueType>
struct apply_value_compare {
typedef T type;
};
};
Now, use_policies will derive from all the template arguments. Where a derived class of VirtualRoot hides a member from the base, that member of the derived class is dominant over the member of the base, and will be used, even though the base-class member can be reached by other path in the inheritance tree.
Note that you don't pay for the virtual inheritance, because you never create an object of type use_policies. You only use virtual inheritance to make use of the dominance rule.
template<typename B, int>
struct Inherit : B { };
template<class Pol1, class Pol2>
struct use_policies : Inherit<Pol1, 1>, Inherit<Pol2, 2>
{ };
Because we potentially derive from the same class more than once, we use a class template Inherit: Inheriting the same class directly twice is forbidden. But inheriting it indirectly is allowed. You can now use this all like the following:
MyClass<int, float> m;
MyClass<float, double, ValueCompareIs< less<double> > > m;