I am doing an export of classes from c++ to python. Specifically, I have to export instances of the class Col
from the libraryArmadillo
(it's something like a std::vector< >
).
This bookstore has a peculiarity. The access functions to the elements of an instance Col< >
can be configured so that they perform the bounds check or not. But such configuration is done using a#define ARMA_NO_DEBUG
. That is, it is done at the file level.cpp
.
It turns out that in my C++ code I am not interested in such a check being performed. But in the code exported to python if . This implies that there will be files .cpp
in which said macro is defined, and others in which it is not.
Since I don't really trust what can happen in this situation, I have created 4 small test files. What I expected is that the linker would protest multiple definitions, or some similar error:
test.hpp
#ifndef TEST_HPP
#define TEST_HPP
#include <stdexcept>
template< typename T > T __attribute__ ((noinline)) dummy( const T &arg );
template< typename T > T __attribute__ ((noinline)) dummy( const T &arg ) {
#ifndef NDEBUG
throw std::runtime_error( "" );
#endif
return T( arg );
}
#endif
debug.cpp
#include "test.hpp"
int debug( int arg ) {
return dummy( arg );
}
nodebug.cpp
#define NDEBUG
#include "test.hpp"
int nodebug( int arg ) {
return dummy( arg );
}
main.cpp
#include <iostream>
extern int nodebug( int );
extern int debug( int );
int main( ) {
int nd = nodebug( 10 );
std::cout << "nodebug: " << nd << std::endl;
int d = debug( 5 );
std::cout << "debug: " << d << std::endl;
return 0;
}
All this compiled with
g++ -std=c++11 -Wall -Wextra -pedantic -O3 nodebug.cpp debug.cpp main.cpp
With all of the above, the idea was to force the and functions to debug( )
be nodebug( )
external (and had to be bound), and to force the same for the template dummy< >( )
. In fact, the order
nm -C main
0000000000001180 t int dummy<int>(int const&) [clone .isra.0]
0000000000001390 t int dummy<int>(int const&) [clone .isra.0]
shows 2 entries for dummy< >( )
with the same template parameters , which I didn't know was possible.
Well, after compiling and linking (which I was surprised it worked), I run el main
and get:
nodebug: 10
terminate called after throwing an instance ofstd::runtime_error
what():
Aborted
Which, things in life, is a result according to my code.
How is this possible ? Shouldn't a linking error have occurred because there are multiple different definitions of my template function dummy< >( )
with different implementations? Have I made a mistake in my test code and am I actually doing something other than what I intended? And finally: Although I am forcing maximum compatibility when compiling ( -pedantic
), is this standard behavior or am I (unknowingly and unintentionally) using some compiler extension?
This program violates the one definition rule . It is not allowed to include two different definitions of the same function, type, or template. If you do that, the behavior is undefined.
This violation does not require diagnosis. The compiler is not required to issue an error or warning.
Undefined behavior can manifest itself in a number of ways, including functioning as expected (always, or only on even days).