In detecting in-inline types in C (REV # 2)
BY Liu Weipeng (Pongba)
C Louvre (http://blog.9cbs.net/pongba)
Motivation
Suppose a university's registration system provides a registration function:
Template
Void Register (T Person)
{
Register (Person, TypeName T :: Person_tag ());
}
And for the registrant has the following identification:
Struct student_tag {};
Struct teacher_tag {};
There are also several overloaded versions for internal use:
Template
Template
And stipulate that the students must be internatically typef student_tag person_tag, teacher class typef teacher_tag person_tag, which, when passing the original Register object for the student class, TypenAme T :: Person_tag () actually constructs a student_tag object, Thus, the function is overloaded, call the Register internal version of the Template
The problem is, now there is more than a student teacher, there are other people such as workers, guards. If they don't have anything in the class typed, Register requires a mechanism to determine if TypeDef has a certain identifier (for example, Person_TAG). If not, it will be processed by default. If so, more detailed classification is performed.
Implementation
This issue may have two implementation pathways.
First, use the function overload, the specific example:
Typedef char (& yes_type) [1]; // sizeof (yes_type) == 1
Typedef char (& no_type) [2]; // sizeof (no_type) == 2
The above two typefef are used to identify different overload functions. Char (&) [1] indicates a reference to the Char [1] array, so sizeof (&) [1]) == SizeOf (char [1]) == 1. Pay attention to a pair of parentheses around the & symbol, if it is not necessary, if the compilation error will cause compilation errors, as Char * [1] will be parsed to the array of char *, CHAR & [1] will be parsed as an array of references, The latter is illegal. The combination of the operator is enclosed by inclusing the cracker, which will be parsed to a reference to the CHAR [1] array.
Template
Struct does_sometypedef_exists
{
Template
Static YES_TYPE CHECK (U, Typename U :: Key_TYPE * = 0); // # 1
Static no_type check (...);
Static t t; // declaration
Static const bool value = sizeof (check (t)) == sizeof (yes_type);
Note that the space between # 1, * and = is necessary, otherwise the compiler resolves it to the Operator * = operator.
In my VC7.0 environment, the following tests were successful:
Struct a {};
Struct B
{
Typedef int key_type;
}
int main ()
{
Std :: cout << does_sometypedef_exisms :: value << '// 0
<< Does_SometypedEf_exists :: value << '' // 1
<< std :: endl;
}
Let me explain its principle for you.
When performing overload parsing, the compiler will first try to instantiate the template function that can match and incorporates them to the candidate single of the function to be overloaded, in this example, when Typename T :: key_type does not exist When Check's first template version cannot be instantiated (because its second parameter type TypenAme U :: Key_Type * does not exist), only the second version can only match. When TypenAme T :: Key_Type exists, the first template function can be instantiated, and can match (note the second parameter is the default parameter), so there is no doubt that the compiler will match the first version because the C standard guarantee: only When all other overload versions do not match the version of any type of parameter list (in this example is NO_TYPE CHECK (...)) will be matched.
A payable place is: CHECK's first version can only be a template function, because when the compiler is derived, it does not instantiate it, not to generate it, rather than generating compilation errors ( Unless there is no other matching overload version). Because compilation errors are only generated during compilation, since the template is not instantiated, the template does not actually compile.
However, if it is not a template function, as the DOES_SOMETYPEDEF_EXISTS class is instantiated. It will also be instantiated, but if there is no T :: Key_Type, the function is illegal.
There is also a note that is worth noting: DOES_SOMEPEDEF_EXISTS inside Static T t; just a statement, does not take up memory space, even if it is a statement, so the compiler does not initialize it, so it's default construct The function will not be executed at all. In fact, the compiler does not even go to see if it has the default constructor it is available, it only needs type information, isn't it? Therefore, even for some reason (for example, if you want to create on the pile) T, the default constructor is disabled (set to private), then the above Traits will not be compiled. "But, wait!" You seem to be aware of the problem: "The parameter of CHECK is a pass value! At this time, if the copy constructor is the private thing, what will happen?" The fact is that there is no need to worry, In the world of SizeOf, no evaluation behavior at all, the compiler only needs information about the type. There is a huge type of derivation system inside the compiler. Regardless of the expression in sizeof (...), the type will eventually be derived correctly in the compile period. For SIZEOF (T)), the compiler has the return value type information of the function. It does not perform the code of the function, and does not do the actual number of campaign, so the copy structure is not from occur. But there is a very weird problem (existed in my VC7.0 environment), suppose we add a new class:
Struct C
{
Template
Struct Key_Type {}; // Please note this is a template class
}
Press, in this case, DOES_SOMEPEDEF_EXISTS
Template
Static YES_TYPE CHECK (U,
Typename u :: key_type * = (Typename U :: Key_TYPE *) 0);
I just added a conversion, and the compiler started complaining that the template class (which refers to C :: Key_Type) requires template parameters. I made another type of test (even I found that if I passed the 10 to the second parameter, the compiler will say that INT can be converted to c :: key_typ *, yes, this is the original text of compilation errors. Is this Indicates that the compiler admits that C :: key_type * is a type? I don't know). The conclusion is that this will happen only when TypenAme U :: Key_TYPE * is used as a parameter type of the template function.
The second implementation is the rules that utilize template bias and default template parameters:
Template
Struct Check_Helper
{
Typedef t type;
}
Template
Struct does_sometypedEf_exists_1
{
STATIC const bool value = false;
}
Template Typename Check_Helper { STATIC const bool value = true; } This looks very small, just use template offset. But please feel patiently listen to me. If TypenAme X :: Key_Type exists (assuming x is arbitrary), DOES_SOMEPEDEF_EXISTS_1 Struct does_sometypedEf_exists_1 TypeName Check_helper TypeName Check_Helper Struct does_sometypedef_exists_1 So, if you test: does_sometypedef_exists_1 According to the above derivation, if TypenAme X :: Key_Type exists, the offset version of DOES_SOMEPEDEF_EXISTS_1 also exists and the form is: Struct does_sometypedef_exists_1 The compiler is then selected to match the offset version, where the value value is TRUE. And if Typename X :: Key_Type does not exist, Typename Check_Helper Test (TEST) Now test our two implementation versions, assume that there are several classes: // No key_type Struct a {}; // typedef Struct b {type;}; // key_type is a member function Struct c {void key_type (void) {}}; // Key_Type is a static constant data member Struct D {static const bool key_type = false; // Definition, D instead of declaration Const Bool D :: Key_Type; // key_type is a template class Struct e { Template Struct key_type {}; } Template Struct does_typedef_exists { Typedef does_sometypedEf_Exists Static const bool value = IMPL_TYPE :: Value; } Int main () { Std :: cout << does_typedef_exists :: value << '' << Does_typedEf_exists :: value << '' << does_typedef_exists << does_typedef_exists << does_typedef_exists << std :: endl; Return 0; } On my VC7.0 compilation platform: If you use the first implementation, this will output: 0 1 0 0 1 If you use the second implementation, this will output: 0 1 0 0 0 Obviously, two implementations are different for Struct E. In fact, we hope that the result of this traits gives the result of 1. From this point, the first implementation has been successful in my compiler, and the second implementation has not yet. In any case, we must try to find a way to implement it. This method is not like achieving a possible "one paste" that relies on the compiler, which should be based on the rule of C standard. Paul Mensonides provides a way, however compiled in my VC7.0 cannot pass. I will introduce it later. IMPROVEMENT The first implementation can also be improved, like this: Template Struct does_sometypedef_exists { Template Static YES_TYPE CHECK (TypeName U :: Key_Type *); Template Static no_type check (...); Static const bool value = sizeof (check } In this way, remove Static T T, and the first parameter of CHECK, which makes the code look more concise and more reliable. Encapsulation Now our Traits can only detect the existence of TypenAme T :: Key_Type, we need an expansion mechanism to detect the existence of the inline type of any name. We use macros: #define import_typedef_exists (id) / Template Struct does_sometypedEf_exists _ ## id / {/ PRIVATE: / Template Static YES_TYPE CHECK (TypenAme U :: ID *); / Template Static no_type check (...); / PUBLIC: / Static const bool value = sizeof (check } #define does_typedef_exists (t, id) / DOES_SOMETYPEDEF_EXISTS _ ## id After this restoration, when you want to detect an inline type of a name such as Some_Type, you will write such a code outside any function: import_typedef_exists (some_type) This will expand into a template class named does_someTypedEf_exists_some_type, then you use it like this: DOES_TYPEDEF_EXISTS (X, Some_TYPE) :: Value; This will detect whether there is some_type in class x. The reason why not :: Value is directly incorporated into the macro is to keep Traits programming style. Paul Mensonides detection method for embedded Template Paul Mensonides is the designer of the Preprocesser section of the Boost library, which is a world of macro and a very delicate part of the BOOST library. I originally saw his answer on this question on comp.lang.c . Moderated. Template // The following is a bias Template Class T, Class T1> // T is a template Struct split Struct Type {}; } Template Class T, Class T1, Class T2> Struct Split Struct Type {}; } // etc.: (, there is a version that supports more template parameters later, Template { Private: Template Static YES_TYPE CHECK TypeName Split < Typename u :: template key_type ); Template Static YES_TYPE CHECK TypeName Split < Typename u :: template key_type ); // etc.: (After the version supports more template parameters, it is slightly Template PUBLIC: Static Const Bool Value = SIZEOF (Check } Template Class Has_Key_Type { Private: Template Template PUBLIC: Static Const Bool Value = SIZEOF (Check } Template { STATIC const bool value = false; } Paul Mensonides said it can work, I also think that it should work according to the standard, but the fact is a lot of complaints in my VC7.0 compiler. I tried other methods, and the result is always similar compilation errors to block me. I hope it works on your compiler. The principle here is that if the type X has an in-line template type definition key_type, the returns in HAS_TEMPLATE_KEY_TYPE has a member function that can match it, while others will not be instantiated (VC7.0 seems to always Trying to instantiate other things, the result will always complain that the template parameter is too small or too much). However, this solution for Paul Mensonides has a problem: if the definition of the embedded template class is like this: Template Struct key_type {}; There will be no overload version that returns YES_TYPE can match it, see the definition of the Split class, its template template template parameter is Template Template Class T, INT T1> Struct Split This is not practical. Because Int and Class may have infinite combinations. If key_type changes to Template Conclusion For the last question I put forward, it seems that there is no good solution. So only the possibility of this embedded template can only be abandoned, and it is assumed that the situation is simple. For the latter, this technique has a good performance.