What are my options for an associative compile-time container?

I need a mechanism that sets the types T1 and T2 , produces the third type T3 , if the pair ( T1 , T2 ) is valid, otherwise a special type Null .

I am currently defining T1 as a class within which I can map a valid set of parameters for T2 to the corresponding T3 .

I am looking for syntax so that a set of valid T2 can be defined inline in the definition of T1 . This is one way to solve the problem using overload resolution:

 #include <utility> struct X {}; struct Y {}; struct A {}; struct B {}; struct C {}; struct S // T1 { X member(A) { return X(); } // T2=A, T3=X Y member(B) { return Y(); } // T2=B, T3=Y }; struct Null { }; template<typename T, typename Arg> decltype(std::declval<T>().member(std::declval<Arg>())) call_member(T& t, Arg arg) { return t.member(arg); } template<typename T> Null call_member(T& t,...) { return Null(); } int main() { S s; X x = call_member(s, A()); // calls S::member(A) Y y = call_member(s, B()); // calls S::member(B) Null null = call_member(s, C()); } 

The task is to handle the case when T2 not found - in the above example, call_member processed. I am trying to avoid the definition of Null S::member(...) .

This example uses decltype , but is there a way to do this in C ++ 03? I am open to any alternative implementations (preferably C ++ 03.)

It would also be possible to implement such a mechanism using explicit specialization, but I am looking for a method that retains the same syntactic structure as in the example, so that it can be expressed as follows:

 #define MEMBER(T2, T3) /* implementation details */ struct S : Base // base-class may contain helper code { MEMBER(A, X) MEMBER(B, Y) }; 
+4
source share
2 answers

In the structure S , it is possible to use a template class. Then you can specialize this class according to your needs:

 struct Null {}; struct S { template<typename> struct map { typedef Null type; }; }; template<> struct S::map<int> { typedef char type; }; template<> struct S::map<char> { typedef int type; }; int main() { std::cout << typeid(S::map<int>::type).name() << std::endl; // c std::cout << typeid(S::map<char>::type).name() << std::endl; // i std::cout << typeid(S::map<S>::type).name() << std::endl; // 4Null } 

The syntax for defining is not quite the way you wanted (you should declare specializations outside the class, and I don't think you can discard the original Null mapping in the base class), but at least it's simple and C ++ 03 compatible A few macros can make it more enjoyable to use, however, something like:

 struct S { INITIALIZE_TYPES_MAP; }; ADD_MAPPED_TYPE(S, int, char); ADD_MAPPED_TYPE(S, char, int); //... std::cout << typeid(GET_MAPPED_TYPE(S, int)).name() << std::endl; 

These macros are trivial to write, so I wonโ€™t bother you.

+1
source

Here is half the solution (map T1 and T2 - T3, but not a card with Null, then invalid)

 // Need a typedef for each valid combination typedef T1_T2 T3; #define MAP(x,y) x ## _ ## y 

If you know all the unacceptable combinations ahead of time, you can do

 typedef T1_T3 Null; typedef T2_T3 Null; 
-one
source

Source: https://habr.com/ru/post/1498837/


All Articles