template
<
template <typename, typename>
class storage_t,
typename T,
typename is_allocated
>
class Buffer : public storage_t<T, is_allocated> { ... };
template
<
template <typename, typename>
class storage_t,
typename T = storage::UnknownType,
typename is_allocated = std::false_type
>
class Example_Buffer
: public Buffer<storage_t, T, is_allocated> {
constexpr Example_Buffer(
typename storage_t<T, is_allocated>::iterator it) {}
};
Example_Buffer<...> inherits from Buffer<...>. Buffer<storage_t, T, is_allocated> inherits from storage_t<T, is_allocated>. storage_t<...> includes typedefs and static constexpr data. Is there a way to access these typedefs and static constexpr data in the constructor of Example_Buffer through inheritance? (through inheritance, that is, not using storage_t<T, is_allocated>? It feels slightly odd to use this syntax twice within the same class.
Feel free to ask if I need to elaborate.
The members are inherited and accessible as long as they are public in storage_t. You can use the injected class name and/or the injected base class name to access these dependent members,
Here are a few options..
#include <iostream>
#include <type_traits>
#include <typeinfo>
using namespace std;
template <typename A,typename B>
struct basic_storage
{
using a_type = A;
using b_type = B;
static constexpr bool value = b_type::value;
};
template
<
template <typename, typename>
class storage_t,
typename T,
typename is_allocated
>
class Buffer : public storage_t<T, is_allocated> {
public:
using storage_type = storage_t<T, is_allocated>;
};
template
<
template <typename, typename>
class storage_t,
typename T /*= storage::UnknownType*/,
typename is_allocated = std::false_type
>
class Example_Buffer
: public Buffer<storage_t, T, is_allocated> {
public:
constexpr Example_Buffer(
/*typename storage_t<T, is_allocated>::iterator it*/) {
//using the members with the injected class name..
using b_type = typename Example_Buffer::b_type;
// or directly using injected class name..
std::cout << typeid(typename Example_Buffer::a_type).name() << std::endl;
std::cout << Example_Buffer::value << std::endl;
// using storage_type defined in Buffer<...>
using storage_type = typename Example_Buffer::storage_type;
std::cout << typeid(typename storage_type::a_type).name() << std::endl;
std::cout << storage_type::b_type::value << std::endl;
std::cout << storage_type::value << std::endl;
}
};
int main() {
Example_Buffer<basic_storage,int,std::true_type>{};
return 0;
}
Demo
If your wondering why you cant just access then in the derived class without prefixing them with Example_Buffer:: base class name, as this post explains it is because they are dependent names.
Related
I need to define a common template class for arithmetic types and pointer types.
following is the code I tried but I never got it correct. I need to implement it using g++4.4.7, because of that I am using boost.
The output should be ARITHMETIC followed by POINTER.
//primary template class
template <class T, class Enable = void>
struct Class
{
};
template <class C>
struct Class<C, typename boost::enable_if_c<boost::is_arithmetic<C>::value || boost::is_pointer<C>::value>::type>
{
static inline typename boost::enable_if_c<boost::is_arithmetic<C>::value, void>::type
print(const C& obj)
{
std::cout << "ARITHMETIC TYPE" << std::endl;
}
static inline typename boost::enable_if_c<boost::is_pointer<C>::value, void>::type
print(const C& obj)
{
Class<uint64_t>::print(reinterpret_cast<const uint64_t&>(obj));
std::cout << "POINTER" << std::endl;
}
};
int main()
{
int x = 0;
Class<int*>::print(&x);
return 0;
}
I made print a template function and it is working as expected.
template <class C>
struct Class<C, typename boost::enable_if_c<boost::is_arithmetic<C>::value || boost::is_pointer<C>::value>::type>
{
template <class T>
static inline typename boost::enable_if_c<boost::is_same<C, T>::value && boost::is_arithmetic<T>::value, void>::type
print(const T& obj)
{
std::cout << "ARITHMETIC TYPE" << std::endl;
}
template <class T>
static inline typename boost::enable_if_c<boost::is_same<C, T>::value && boost::is_pointer<T>::value, void>::type
print(const T& obj)
{
Class<uint64_t>::print(reinterpret_cast<const uint64_t&>(obj));
std::cout << "POINTER" << std::endl;
}
};
I have a template class(CrMultiIndex) that receive as template parameter a definition of boost multi index(GlobalHash).
I need :
To add statistics to my template class according to Index used.
So i need a way to resize the vector(m_StatsByIndex) at init with the number of existing indices.
I still want the user to search according to tag and not index number.
So i need a way to convert from tag to index number so i can update statistics in vector according to index in vector.
I have template class
template <typename KeysType, typename MultiIndexType>
class CrMultiIndex
{
std::vector<SrStatisticsByIndex> m_StatsByIndex;
public:
MultiIndexType *m_pMultiIndex=NULL;
CrMultiIndex()
{
m_pMultiIndex = new MultiIndexType(typename
MultiIndexType::ctor_args_list());
}
Here is the definition of boost multi index container:
typedef boost::multi_index::multi_index_container<
CrUsersKeys,
UsersKey_hash_indices/*,
bip::allocator<CrUsersKeys,bip::managed_shared_memory::segment_manager>*/
> GlobalHash;
with a search function according to Tag
template <typename TagType,typename SearchingKey>
typename MultiIndexType::template index<TagType>::type::iterator
GetIteratorBy(SearchingKey & key)
{
return m_pMultiIndex->template get<TagType>().find(key) ;
}
Code is at http://coliru.stacked-crooked.com/a/d97195a6e4bb7ad4
You'd need to query the embedded index type lists:
typedef typename MultiIndexType::index_type_list::size NumberOfIndexes;
template <typename Tag> constexpr static size_t IndexOfTag() {
namespace mpl = boost::mpl;
using tl = typename MultiIndexType::index_type_list;
using B = typename mpl::begin<tl>::type;
using helper = typename MultiIndexType::template index<Tag>;
static_assert(helper::index_found, "index not found");
auto N = mpl::distance<B, typename helper::iter>::value;
return N;
}
Or, using Boost Mpl all the way:
typedef typename MultiIndexType::index_type_list::size NumberOfIndexes;
template <typename Tag> constexpr static size_t IndexOfTag() {
namespace mpl = boost::mpl;
using tl = typename MultiIndexType::index_type_list;
using B = typename mpl::begin<tl>::type;
using E = typename mpl::end<tl>::type;
using It = typename mpl::find_if<tl, bmi::detail::has_tag<Tag> >::type;
static_assert(not std::is_same<E, It>(), "index not found");
auto N = mpl::distance<B, It>::value;
return N;
}
You can use it like so:
template <typename TagType, typename SearchingKey>
typename MultiIndexType::template index<TagType>::type::iterator
GetIteratorBy(SearchingKey &key) {
auto& idx = m_pMultiIndex.template get<TagType>();
auto& stats = GetStats<TagType>();
auto it = idx.find(key);
++(it == idx.end()? stats.searchedNotFound : stats.searchedSuccessfully);
return it;
}
DEMO
Note the code has been simplified:
Live On Coliru
#include <iostream>
#include <boost/multi_index/member.hpp> // for member
#include <boost/multi_index/hashed_index.hpp> // for hashed_unique
#include <boost/multi_index/ordered_index.hpp> // for ordered_non_unique
#include <boost/multi_index_container.hpp> // for multi_index_container
namespace bmi = boost::multi_index;
struct SrStatisticsByIndex {
int deleted;
int searchedSuccessfully;
int searchedNotFound;
};
template <typename MultiIndexType, typename ValueType = typename MultiIndexType::value_type>
class CrMultiIndex {
typedef typename MultiIndexType::index_type_list::size NumberOfIndexes;
template <typename Tag> constexpr static size_t IndexOfTag() {
using tl = typename MultiIndexType::index_type_list;
using B = typename boost::mpl::begin<tl>::type;
using helper = typename MultiIndexType::template index<Tag>;
static_assert(helper::index_found, "index not found");
return boost::mpl::distance<B, typename helper::iter>::value;
}
public:
MultiIndexType m_pMultiIndex;
template <typename Tag> SrStatisticsByIndex& GetStats()
{ return m_StatsByIndex.at(IndexOfTag<Tag>()); }
template <typename Tag> SrStatisticsByIndex const& GetStats() const
{ return m_StatsByIndex.at(IndexOfTag<Tag>()); }
// All the protected function are non locking function
template <typename TagType, typename SearchingKey>
typename MultiIndexType::template index<TagType>::type::iterator
GetIteratorBy(SearchingKey &key) {
auto& idx = m_pMultiIndex.template get<TagType>();
auto& stats = GetStats<TagType>();
auto it = idx.find(key);
++(it == idx.end()? stats.searchedNotFound : stats.searchedSuccessfully);
return it;
}
void Insert(ValueType const &key) {
std::cout << (m_pMultiIndex.insert(key).second? "success":"failed") << std::endl;
}
private:
std::vector<SrStatisticsByIndex> m_StatsByIndex { NumberOfIndexes() };
};
class CrUsersValue {
int val1;
int val2;
};
class CrUsersKeys {
public:
int IMSI;
int TIMESTAMP;
CrUsersValue val;
};
typedef boost::multi_index::multi_index_container<
CrUsersKeys,
bmi::indexed_by<
bmi::ordered_non_unique<bmi::tag<struct TIMESTAMP_tag>,
bmi::member<CrUsersKeys, int, &CrUsersKeys::TIMESTAMP> >,
bmi::hashed_unique<bmi::tag<struct IMSI_tag>,
bmi::member<CrUsersKeys, int, &CrUsersKeys::IMSI> /*, boost::hash<int>, std::equal_to<int>*/>
>
/*, bip::allocator<CrUsersKeys,bip::managed_shared_memory::segment_manager>*/
>
GlobalHash;
int main() {
CrMultiIndex<GlobalHash> multi;
CrUsersKeys key;
key.IMSI = 2;
multi.Insert(key);
int searchKey = 2;
auto it = multi.GetIteratorBy<IMSI_tag>(searchKey);
if (it != multi.m_pMultiIndex.get<IMSI_tag>().end())
std::cout << "found " << std::endl;
}
Prints
success
found
As a supplement to sehe's answer, this a rewrite of IndexOfTag that does not depend on undocumented Boost.MultiIndex features:
Live On Coliru
template<typename MultiIndexContainer,std::size_t N=0>
struct index_position:index_position<MultiIndexContainer,N+1>
{
using index_type=typename boost::multi_index::nth_index<MultiIndexContainer,N>::type;
using index_position<MultiIndexContainer,N+1>::case_of;
static constexpr std::size_t case_of(std::in_place_type_t<index_type>){return N;}
};
template<typename MultiIndexContainer>
struct index_position<
MultiIndexContainer,
boost::mpl::size<typename MultiIndexContainer::index_type_list>::value
>
{
static constexpr void case_of(...){}
};
template <typename MultiIndexContainer,typename Tag>
constexpr std::size_t IndexOfTag()
{
using index_type=typename boost::multi_index::index<MultiIndexContainer,Tag>::type;
return index_position<MultiIndexContainer>::case_of(std::in_place_type<index_type>);
}
Edit: In C++14:
Live On Coliru
template<typename MultiIndexContainer,std::size_t N=0>
struct index_position:index_position<MultiIndexContainer,N+1>
{
using index_type=typename boost::multi_index::nth_index<MultiIndexContainer,N>::type;
using index_position<MultiIndexContainer,N+1>::case_of;
static constexpr std::size_t case_of(index_type*){return N;}
};
template<typename MultiIndexContainer>
struct index_position<
MultiIndexContainer,
boost::mpl::size<typename MultiIndexContainer::index_type_list>::value
>
{
static constexpr void case_of(...){}
};
template <typename MultiIndexContainer,typename Tag>
constexpr std::size_t IndexOfTag()
{
using index_type=typename boost::multi_index::index<MultiIndexContainer,Tag>::type;
return index_position<MultiIndexContainer>::case_of((index_type*)(nullptr));
}
Here is the specifics:
Consider a simple constructor in a class with two input arguments,
concreteclass(_1, _2).
I have a map for this instantiation, map <string, concreteclassType>.
Also, these classes work with different datatypes concreteclass<double>(_1,_2) is different from concreteclass<int>(_1,_2).
Now that my problem is described above here is what I try to do using boost::factory pattern, classes defined in a string map and datatypes defined in an enum.
First, there is a simple way to demonstrate how boost factory pattern can be used with constructor arguments, the following nicely code works:
// Factory which takes two arguments
struct base {
base(int alpha) : alpha(alpha) {}
virtual ~base() = default;
virtual void print() const = 0;
int alpha;
};
struct derived : public base {
derived(int alpha, int beta) : base(alpha), beta(beta) {}
void print() const override {
std::cout << alpha << " " << beta << std::endl;
}
int beta;
};
void TestBoostFactoryWithTwoArgs()
{
// Constructor factory with two input args
{
std::map<std::string, boost::function<base* (int&, int&)>> factories;
factories["derived"] = boost::bind(boost::factory<derived*>(), _1, _2);
int x = 42;
int y = 51;
std::unique_ptr<base> b{ factories.at("derived")(x,y) };
b->print();
}
// Factory with two initialized inputs args - binding of values not at run time
{
std::map<std::string, boost::function<base* ()>> factories;
factories["derived"] = boost::bind(boost::factory<derived*>(), 42, 51);
std::unique_ptr<base> b{ factories.at("derived")() };
b->print();
}
}
Now consider my code - SimpleClasses.h:
// Dummy base class - non template
class IBaseClass
{
public:
};
// Templatized Derived Base class
template <typename T>
class ConcreteClass : public IBaseClass
{
private:
std::shared_ptr<IBaseClass> m_leftArgument;
std::shared_ptr<IBaseClass> m_leftArgument;
public:
ConcreteClass(std::unique_ptr<IBaseClass>& leftArgument, std::unique_ptr<IBaseClass>& rightArgument)
{
m_leftArgument = leftArgument;
m_rightArgument = rightArgument;
};
virtual T DoSomething()
{
cout << "I did something in Concrete Base Class" << endl;
return T();
}; // This is the main reason for creating T
};
template <typename T>
class ConcreteClassA : ConcreteClass
{
};
template <typename T>
class ConcreteClassB : ConcreteClass
{
};
template <typename T>
class ConcreteClassC : ConcreteClass
{
};
Another File, ClassFactory.h :
#pragma once
#include "SimpleClasses.h"
#include <memory>
#include <map>
#include <boost/functional/overloaded_function.hpp>
#include <boost/functional/factory.hpp>
using namespace std;
// Add More class Keys here
namespace MyClassesNamespace { // These are all string keys
static const string CLASS_A = "specialclassA";
static const string CLASS_B = "specialclassB";
static const string CLASS_C = "specialclassC";
};
enum EMyDataTypes
{
INT8,
FLOAT8,
FLOAT16,
};
// This type def we keep for non templatized base class constructor
typedef boost::function<IBaseClass*(std::unique_ptr<IBaseClass>&, std::unique_ptr<IBaseClass>&)> IBaseClassConstructorFunc_factory;
// Dummy base factory - no template
class UBaseClassTemplateFactory
{
public:
};
template<typename T>
class UClassFactoryTemplate : public UBaseClassTemplateFactory
{
private:
static std::map<string, IBaseClassConstructorFunc_factory> ClassFactoryTemplateMap; // Unique Classes only
public:
UClassFactoryTemplate();
__forceinline static UClassFactoryTemplate*Get()
{
static UClassFactoryTemplate<T> SingletonInstance;
return &SingletonInstance;
}
static std::unique_ptr<IBaseClass<T>> CreateClassTemplatized(string ClassString, std::unique_ptr<IBaseClass> LeftArgument, std::unique_ptr<IBaseClass> RightArgument);
};
// This type def we keep for non templatized base class
typedef boost::function<UBaseClassTemplateFactory*()> ClassFactoryTemplate_factory;
/* This is the instance class that resolves the classes as well as the concrete datatype to be used in UClassFactoryTemplate*/
class UClassFactory
{
private:
UClassFactory();
static std::map<EMyDataTypes, ClassFactoryTemplate_factory> ClassDataTypeTemplateFactoryMap;
public:
__forceinline static UClassFactory *Get()
{
static UClassFactory SingletonInstance;
return &SingletonInstance;
}
static std::unique_ptr<IBaseClass> CreateConcreteClass(string ClassString, std::unique_ptr<IBaseClass> LeftVal, std::unique_ptr<IBaseClass> RightVal, EMyDataTypes someEnumVal = EMyDataTypes::INT8);
};
Finally, in ClassFactory.cpp
#include "ClassFactory.h"
#include <boost/bind.hpp>
/*static, but non-const data members should be defined outside of the class definition
*and inside the namespace enclosing the class. The usual practice is to define it in
*the translation unit (*.cpp) because it is considered to be an implementation detail.
*Only static and const integral types can be declared and defined at the same time (inside class definition):*/
template<typename T>
std::map<string, IBaseClassConstructorFunc_factory> UClassFactoryTemplate<T>::ClassFactoryTemplateMap;
std::map<EMyDataTypes, ClassFactoryTemplate_factory> UClassFactory::ClassDataTypeTemplateFactoryMap;
template<typename T>
inline UClassFactoryTemplate<T>::UClassFactoryTemplate()
{
ClassFactoryTemplateMap[MyClassesNamespace::CLASS_A] = boost::bind(boost::factory<ConcreteClassA<T>*>(), _1, _2);
ClassFactoryTemplateMap[MyClassesNamespace::CLASS_B] = boost::bind(boost::factory<ConcreteClassB<T>*>(), _1, _2);
ClassFactoryTemplateMap[MyClassesNamespace::CLASS_C] = boost::bind(boost::factory<ConcreteClassC<T>*>(), _1, _2);
}
template<typename T>
std::unique_ptr<IBaseClass<T>> UClassFactoryTemplate<T>::CreateClassTemplatized(string ClassString, std::unique_ptr<IBaseClass> LeftArgument, std::unique_ptr<IBaseClass> RightArgument)
{
std::unique_ptr<IBaseClass<T>> someTemplatizedDataTypeInstance{ ClassFactoryTemplateMap.at(ClassString) (LeftArgument,RightArgument) };
return someTemplatizedDataTypeInstance;
}
UClassFactory::UClassFactory()
{
ClassDataTypeTemplateFactoryMap[EMyDataTypes::INT8] = boost::bind(boost::factory<UClassFactoryTemplate<int>*>());
ClassDataTypeTemplateFactoryMap[EMyDataTypes::FLOAT8] = boost::bind(boost::factory<UClassFactoryTemplate<float>*>());
ClassDataTypeTemplateFactoryMap[EMyDataTypes::FLOAT16] = boost::bind(boost::factory<UClassFactoryTemplate<double>*>());
}
std::unique_ptr<IBaseClass> UClassFactory::CreateConcreteClass(string ClassString, std::unique_ptr<IBaseClass> LeftVal, std::unique_ptr<IBaseClass> RightVal, EMyDataTypes someEnumVal)
{
std::unique_ptr<UBaseClassTemplateFactory> BaseOperatorTempFactory{ ClassDataTypeTemplateFactoryMap.at(someEnumVal) };
return BaseOperatorTempFactory->Get()::CreateClassTemplatized(ClassString, LeftVal, RightVal);
}
The question now is, the above code does not even compile let alone run, it says abstract class cannot be instantiated for the templatized map. I just want the UClassFactory to return me correct instantiated class like A,B,C based on a string map with correct datatypes based on an enum. How do I achieve this combination? I wonder what is the correct syntax? Or is my approach inherently flawed? Or there is a nice way to instantiate classes with factory pattern and different datatypes? Please let me know any suggestions/ comments.
Thanks
Alam
How can I have this effect without the arbitrary typedefs?
#include <type_traits>
#include <iostream>
typedef int Primary;
typedef float Secondary;
template<Class C, std::enable_if<std::is_same<Class, Primary>::value || std::is_same<Class, Secondary>::value> = 0>
class Entity {
public:
template<std::enable_if<std::is_same<Class, Secondary>::value>::type = 0>
void onlyLegalForSecondaryEntities() {
std::cout << "Works" << std::endl;
}
};
int main() {
Entity<Secondary> e;
e.onlyLegalForSecondaryEntities();
return 0;
}
Is there a more elegant way to produce this so that Entity can only be instantiated with Primary or Secondary as template arguments?
After fixing the errors in your code:
In C++1z you can easily roll a trait is_any with std::disjunction:
template<typename T, typename... Others>
struct is_any : std::disjunction<std::is_same<T, Others>...>
{
};
In C++11, you can implement disjuncation as
template<class...> struct disjunction : std::false_type { };
template<class B1> struct disjunction<B1> : B1 { };
template<class B1, class... Bn>
struct disjunction<B1, Bn...>
: std::conditional<B1::value != false, B1, disjunction<Bn...>>::type { };
Then define your class template as
template<class C, typename std::enable_if<is_any<C, Primary, Secondary>::value>::type* = nullptr>
class Entity {
public:
template<typename std::enable_if<std::is_same<C, Secondary>::value>::type* = nullptr>
void onlyLegalForSecondaryEntities() {
std::cout << "Works" << std::endl;
}
};
demo
You can take this further and make enable_if_any alias that would resolve to void if possible:
template<typename This, typename... Elems>
using enable_if_is_any = typename std::enable_if<is_any<This, Elems...>::value>::type;
template<class C, enable_if_is_any<C, Primary, Secondary>* = nullptr>
class Entity {
public:
template<typename std::enable_if<std::is_same<C, Secondary>::value>::type* = nullptr>
void onlyLegalForSecondaryEntities() {
std::cout << "Works" << std::endl;
}
};
demo
I have made this for SFINAE:
// Type 'type' exists iff X is a base of COLLECTION
template<typename X, typename COLLECTION, typename RET_TYPE = void>
struct enable_if_is_base_of : std::enable_if<std::is_base_of<X, COLLECTION>::value, RET_TYPE>
{};
Which works well for something like this:
class A {}; class B {}; class C{};
class collection : A, B {};
template <typename X>
typename enable_if_is_base_of<X, collection>::type fn(X&& x) { }
int main() {
fn(A());
fn(B());
// fn(C()); // Fails as expected
return 0;
}
However, if I want collection to be derived from a template as well as non-template classes, I'm not sure how I would go about that.
Something like this:
class A {};
template <typename X> class B {};
class C {};
template <typename X> class D {};
template <typename X> class collection : A, B<X> {};
// Stuff where the magic happens
...
// Testing the magic
template <typename X> // or other template declaration
typename enable_if_is_base_of<X, collection>::type fn(X&& x) { }
// May require more than one fn() declaration.
int main() {
fn(A());
fn(B<int>());
// fn(C()); // Fails to compile
// fn(D<int>()); // Fails to compile
return 0;
}
Bonus point if the collection class can derive from any class or class template with any number of template parameters. :)
After much trying and testing I've come up with a solution:
#include<iostream>
#include<type_traits>
using namespace std;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Place Holder Class
template <typename ...T>
class PH {};
// Select a Collection to Test On
// Since a collection is a template class that can derive from a non-template class
// or template classes of an arbitrary number of typename parameters, these
// functions selects one instance of the collection, for which the base can be
// tested for.
template <
template <template <typename...> class, typename...> class C
, template <typename...> class TT
, typename...T>
C<TT,T...> collection_selector(TT<T...>);
template <template <template <typename...> class, typename...> class C, typename T>
C<PH,T> collection_selector(T);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Aliases the type to make it more readable
template <typename T, template <template <typename...> class, typename...> class C>
is_base_of<T, decltype(collection_selector<C>(declval<T&>()))> enable_if_is_base_of_truth_selector();
// Not doing a direct 'using' to a type because of a bug in VC++ 2013.
// See https://connect.microsoft.com/VisualStudio/feedback/details/1069557/failed-to-deduce-type-for-template-function
template <typename T, template <template <typename...> class, typename...> class C>
using is_base_of_truth = decltype(enable_if_is_base_of_truth_selector<T, C>());
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Aliases the type to make it more readable
template <typename T, template <template <typename...> class, typename...> class C, typename RT>
typename enable_if<is_base_of<T, decltype(collection_selector<C>(declval<T&>()))>::value, RT>::type enable_if_is_base_of_selector();
//typename enable_if<is_base_of_truth<T, C>::value, RT>::type enable_if_is_base_of_selector();
// Not doing a direct 'using' to a type because of a bug in VC++ 2013.
// See https://connect.microsoft.com/VisualStudio/feedback/details/1069557/failed-to-deduce-type-for-template-function
template <typename T, template <template <typename...> class, typename...> class C, typename RT = void>
using enable_if_is_base_of = decltype(enable_if_is_base_of_selector<T, C, RT>());
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Select type from list
template <unsigned int I, typename ...Ts>
struct items;
template <typename T, typename ...Ts>
struct items<0, T, Ts...>
{
typedef T type;
};
template <unsigned int I, typename T, typename ...Ts>
struct items<I, T, Ts...> : items<I-1, Ts...>
{
};
template <unsigned int I>
struct items<I>
{
typedef int type;
};
template <unsigned int I, typename ...Ts>
typename items<I, Ts...>::type get_item_selector();
template <unsigned int I, typename ...Ts>
using get_item = decltype(get_item_selector<I, Ts...>());
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Classes to test on
class A{};
template <typename T> class B{};
template <typename T0, typename T1> class BB{};
class C{};
template <typename T> class D{};
template <typename T0, typename T1> class DD{};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Collection of valid types
template <
template <typename...> class TT
, typename...Ts>
class collection
: A // class A
, B<get_item<0,Ts...>> // class B<X>
, BB<get_item<0,Ts...>, get_item<1,Ts...>> // class BB<Y, Z>
{};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Function to enable on
template <typename T>
enable_if_is_base_of<T, collection> test(T&&)
{
}
int main()
{
cout << "-------------------------------------------------" << endl;
cout << "Should all be 1." << endl;
cout << is_base_of_truth<A , collection>::value << endl;
cout << is_base_of_truth<B<char*> , collection>::value << endl;
cout << is_base_of_truth<BB<int, float>, collection>::value << endl;
cout << "-------------------------------------------------" << endl;
cout << "Should all be 0." << endl;
cout << is_base_of_truth<C , collection>::value << endl;
cout << is_base_of_truth<D<char> , collection>::value << endl;
cout << is_base_of_truth<DD<int, float&>, collection>::value << endl;
cout << "-------------------------------------------------" << endl;
// These succeed to compile since C, D<X> and DD<Y, Z> are the base of collection
test(A());
test(B<int>());
test(BB<int, long>());
// These fail to compile since C, D<X> and DD<Y, Z> are NOT the base of collection
//test(C()); // fails as expected
//test(D<int>()); // fails as expected
//test(DD<int, long>()); // fails as expected
}
DEMO
NOTE: I had to change the template of the collection so that it included a template as well. This made life very interesting but it did make the final result more readable and made some interesting meta functions.