In detecting in C ++, the inline is present (REV # 2)

xiaoxiao2021-03-06  85

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 Void Register (Tp, Student_tag) {...} // Registered Student

Template Void Register (T P, Teacher_tag) {...} // Registered Teacher

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 Void Register (T P, Student_tag) version. Other situations are also available. This is a commonly used technique in generic program (static polymorphism), and it is not seen in STL.

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 :: Value should be false, because the first overload version of TypenAme u :: key_type * cannot be derived as c :: key_type * (c :: key_type is a template, it It is necessary to instantiate the template parameter), however it is compiled under my VC7.0, and the result is true (that is, the overload resolution is the first CHECK function). If I make a little change in the first version of CHECK, like this:

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 struct does_sometypedef_exists_1

Typename Check_Helper :: Type>

{

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 first matches the template parameter T of DOES_SOMETYPEDEF_EXISTS_1 to X, and its offset version is therefore derived as:

Struct does_sometypedEf_exists_1

TypeName Check_helper :: type>

TypeName Check_Helper :: Type is actually X, so the offset version is actually derived as:

Struct does_sometypedef_exists_1

So, if you test: does_sometypedef_exists_1 :: value, according to DOES_SOMEPEDEF_EXISTS_1 default definition (second template parameter default to T), you write equivalent to: does_sometypedEf_exists_1 :: value.

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 :: type is not there, the Diaes_SometypedEf_exists_1 version of the offset version does not exist, so the compiler will Select Use the default definition, where the value value is false. This is the result we want.

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 IMPL_TYPE;

Static const bool value = IMPL_TYPE :: Value;

}

Int main () {

Std :: cout << does_typedef_exists :: value << ''

<< Does_typedEf_exists :: value << ''

<< does_typedef_exists :: value << ''

<< does_typedef_exists :: value << ''

<< does_typedef_exists :: Value << ''

<< 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 (0)) == sizeof (yes_type);

}

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 (0)) == sizeof (Yes_TYPE); /

}

#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 struct split; // default declaration, because it is not to be matched, no definition

// The following is a bias

Template