How to find duplicate definitions from specialized patterns?

I have a specialization template class defined in another file. Therefore, you can create two versions of the same class: once, replacing the template parameter and once using specialization. My real understanding is that this can lead to two instances of the same type having different sizes in memory, which will lead to segmentation errors.

I created a minimal example, and the following code should illustrate the question:

Create a template class:

// - templateexample.h --------------- #ifndef TEMPLATEEXAMPLE_H #define TEMPLATEEXAMPLE_H template<typename T> class Example { public: Example(){} int doWork() {return 42;} }; #endif // ----------------------------------- 

Template specialization in another file:

 // - templatespecialization.h -------- #ifndef TEMPLATESPECIALIZATION_H #define TEMPLATESPECIALIZATION_H #include "templateexample.h" template<> class Example<int> { public: Example() : a(0), b(1), c(2), d(3) {} int doWork() {return a+b+c+d;} private: int a; //<== the specialized object will be larger in memory int b; int c; int d; }; #endif // -------------------------------- 

Have a class that includes only the template class definition, but should include specialization.

 // - ah -------------------------- #ifndef A_H #define A_H #include "templateexample.h" class A { public: Example<int> returnSmallExample(); }; #endif // - a.cpp ------------------------ #include "ah" Example<int> A::returnSmallExample() {return Example<int>();} // -------------------------------- 

The main class now knows two versions of Example<int> of that of A and one of templatespecialization.h.

 // - main.cpp --------------------- #include <iostream> #include "ah" #include "templatespecialization.h" int main() { A a; Example<int> test = a.returnSmallExample(); std::cout<<test.doWork()<<std::endl; } // -------------------------------- 

Please note that this problem only occurs when class A is compiled separately, this example is from ideone 6 outputs, while using separate files can lead to segmentation phages or output 42 ( https://ideone.com/3RTzlC ). On my machine, the example compiles successfully and outputs 2013265920:

proof

In the production version of the above example, everything is built into the shared library, which is used mainly.

Question 1: Why does the linker not detect this problem? This should be easy to detect by comparing the size of objects.

Question 2: is there a way to check object files or a shared library to detect multiple implementations of the same type as in the above example?


Edit: note: the above code is a minimal example to explain the problem. The reason for the situation is that the template class is from one library, and I cannot edit files from this library. Finally, all this is used throughout the executable, and now I need to find out if the problem occurs above.


Edit: The code above can be compiled as follows:

 #!/bin/bash g++ -g -c a.cpp g++ -g -c main.cpp g++ -o test ao main.o 
+8
c ++ templates shared-libraries
source share
4 answers

You have a different definition of the same template and its specialization in different translation units. This leads to a violation of rule 1 of the rule.

The fix would be to put the specialization in the same header file where the primary class template will be defined.

Question 1: Why does the linker not detect this problem? This should be easy to detect by comparing the size of objects.

Different types can have the same size (for example, double and int64_t ), so obviously, just comparing the sizes of objects does not work.

Question 2: is there a way to check object files or a shared library to detect multiple implementations of the same type as in the above example?

You should use the gold linker to link your C ++ applications if you are no longer using it. One nice feature is the --detect-odr-violations command line switch, which does exactly what you ask for:

gold uses heuristics to search for possible ODR violations: if the same symbol is displayed in two different input files, and two symbols have different sizes, then gold looks through debugging information in the input objects. If debugging information suggests that the characters were defined in different source files, gold reports a possible ODR violation. This approach has both false negatives and false positives. Nevertheless, it is quite reliable in detecting problems when binding non-optimized code. It is much easier to find these problems during communication than to debug cases where the wrong character.

See "Enforcing a definition rule" for details.

+7
source share

Question 1 : Why does the linker not detect this problem? This should be easy to detect by comparing the size of objects.

Because this is not a linker problem. In the case of templates, the main declaration and all other specializations (be it a class or function) should be visible upfront .

Question 2 : is there a way to examine object files or a shared library to detect multiple implementations of the same type as in the example above?

At least I don’t know anything.

To further simplify this situation, look at a similar broken code:

 // foo.h inline void foo () { #ifdef FOO return; #else throw 0; #endif } // foo1.cpp #define FOO #include"foo.h" // ... uses foo() with "return" // foo2.cpp #include"foo.h" // ... uses foo() with "throw" 

You may get different results based on the compilation method.

Update :
Having multiple body definitions for the same function is an undefined behavior . This is the reason you get an uncomfortable output, for example 2013265920 , and the same thing happens on my machine. The output should be either 42 or 6 . I gave you the example above, because using inline functions you can create such race conditions.

Due to my limited knowledge at the linking stage, the responsibility performed by a typical linker is limited only to the rejection of more than 1 non-built-in function definitions with the same signature. eg.

 // Header.h is included in multiple .cpp files void foo () {} // rejected due to multiple definitions inline void bar () {} // ok because `inline` keyword is found 

In addition, it does not check whether the function body is similar or not, because it is already being analyzed at an early stage, and the linker does not analyze the function body.
Having said above, now pay attention to this statement:

template functions are always inline by nature

Therefore, the linker cannot get a chance to reject them.

The safest way is to #include read-only header in your custom header and include that custom header everywhere.

+1
source share

I don’t know how to do this by analyzing the compiled binary, but you could plot your #include links - there are tools that can do this, such as Doxygen, and use it to search for files that (directly or indirectly) include the library title itself, but not the specialization title.

You will need to examine each file to determine if it really uses the given template, but at least you can narrow down the set of files that you need to examine.

0
source share

I think you managed to trick the compiler. But I must note that, in my humble opinion, you misunderstand the concept of templates, in particular, you are trying to mix template specialization with inheritance. I mean, template specialization should not add data members to a class, the only goals are to define types for function parameters and class fields. If you want to change the algorithms, that is, rewrite the code or add new date members to the class, you must define a derived class. Regarding the "several steps" of a separate compilation and template in the library, a link to C ++ ( http://www.cplusplus.com/doc/oldtutorial/templates/ ):

Since templates are compiled when necessary, this limits multifactor projects: the implementation (definition) of a class or function of a template must be in the same file as its declaration. This means that we cannot separate the interface in a separate header file and that we must include both the interface and the implementation in any file that uses templates. Since the code is not generated until the template is instantiated, when necessary, the compilers are ready to allow the inclusion of more than once from the same template file with the declaration and definitions in the project without binding errors.

0
source share

All Articles