Explicit Specialization of non-template member function is not called - c++

...unless something else from the library is called. Here is a minimal example.
test1.cpp
#include <iostream>
void proofOfTwoLinked();
template <class T>
struct Foo
{
void bar(){ std::cout << "normal bar\n"; }
};
struct A{};
struct B{};
struct C{};
struct D{};
template <> void Foo<B>::bar(){ std::cout << "B bar\n"; }
int main()
{
Foo<A> a;
Foo<B> b;
Foo<C> c;
Foo<D> d;
a.bar();
b.bar();
c.bar();
d.bar();
//proofOfTwoLinked();
}
test2.cpp
#include <iostream>
struct C;
template <class T>
struct Foo
{
void bar(){ std::cout << "normal bar\n"; }
};
template <> void Foo<C>::bar(){ std::cout << "C bar\n"; }
void proofOfTwoLinked()
{
std::cout << "Yup, two is linked\n";
}
If I compile the two together, the program works as expected:
$ rm test; rm *.a; rm *.o; g++ -c test1.cpp; g++ -c test2.cpp; g++ -o test test1.o test2.o; ./test
normal bar
B bar
C bar
normal bar
If I compile test2, put it in an archive, and then link the program against that... the specialization for type C is not executed when c.bar() is called:
$ rm test; rm *.a; rm *.o; g++ -c test1.cpp; g++ -c test2.cpp; ar -r test2.a test2.o; g++ -o test test1.o test2.a; ./test
ar: creating test2.a
normal bar
B bar
normal bar
normal bar
But if I uncomment the last function call of test1 (proofOfTwoLinked) and then compile again, the specialization is executed.
$ rm test; rm *.a; rm *.o; g++ -c test1.cpp; g++ -c test2.cpp; ar -r test2.a test2.o; g++ -o test test1.o test2.a; ./test
ar: creating test2.a
normal bar
B bar
C bar
normal bar
Yup, two is linked
This strikes me as weird, and is certainly contrary to my expectation. Is this in fact normal behavior? Perhaps since there already exists some form of every function that is called in main() before the linker searches test2.a it skips the archive. Is there a way to force the linker to "look at the whole archive"?
I'm using gcc 4.6.1 and ar 2.21.53 (in ubuntu).

Using MSVC2010SP1 I get slightly different results:
compiling together as is I don't get "C bar". This is as expected because test1.cpp and test2.cpp are separate compilation units and with no forward declarations of the specializations that the other contains test1.cpp will instantiate its the default "normal bar" and test2.cpp will not instantiate the "C bar" because it can't see anything using it.
when I uncomment proofOfTwoLinked(); i get "Yup, two is linked" which is expected because "proofOfTwoLinked()" is forward declaired. I still don't get "C bar" which is as expected because it is not forward declaired in test1.cpp
When I compile again adding
template <> void Foo<C>::bar();
to test1.cpp I get a linker error because although the test1.cpp compilation unit now knows there is a
template <> void Foo<C>::bar()
out there somewhere, test2.cpp still does not know that anyone is using it.
When I compile again adding
template void Foo<C>::bar();
to test2.cpp everything works and I get "C bar". Note that
template void Foo<C>::bar();
must be BEFORE its definition.
As far as I can tell MSVC is acting correctly and gcc is acting weird in your case. I used http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf section 14.7 as a reference, it might be helpful.

Related

Is it necessary to annotate the template specializations of an exported entity with export?

… or is it enough to declare them at the module interface unit?
test.cpp:
module;
#include <iostream>
export module M;
export
template<typename>
class T
{
public:
void foo() const
{ std::cout << "T" << std::endl; }
};
// export <-- NOT exported
template<>
class T<int>
{
public:
void foo() const
{ std::cout << "T<int>" << std::endl; }
};
main.cpp:
import M;
int main()
{
T<int> x;
x.foo();
return 0;
}
Output:
$ rm -f gcm.cache/* && $(HOME)/gcc-master/bin/g++ -O3 -fmodules-ts test.cpp main.cpp -o foo && ./foo
T<int>
$ $(HOME)/gcc-master/bin/g++ --version
g++ (GCC) 11.0.0 20210128 (experimental)
Exporting affects only name lookup and linkage. Neither of those is relevant to any kind of template specialization or instantiation, so they never need exporting.

How to use gcc options --wrap=symbol in class method

This is a simple example that can help me print malloc params value
void *__wrap_malloc(size_t size)
{
printf("My malloc function!\n");
return __real_malloc(size);
}
But I want to use in class method, like that
class A{
public:
void test(int a)
{
printf("%d", a):
}
}
This using type gives an error.
void A::__wrap_test(int a)
{
printf("wrap");
return A::test()
}
Can someone tell me how to use it correctly?
how to use it correctly?
To use at all, you first have to have a symbol. So first move the inline function definition to be non-inline so a symbol may be generated for it. Then you need to find the mangled name of the symbol. So first correct your typos and compile the library to an object file:
// a.hpp
class A {
public:
void test(int a);
};
// a.cpp
#include <stdio.h>
#include "a.hpp"
void A::test(int a) {
printf("%d\n", a);
}
After compilation check the generated mangled symbol name:
$ g++ -c a.cpp && nm a.o
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T _ZN1A4testEi
U printf
So here you can finally get the name of the symbol to wrap: _ZN1A4testEi. Then you can create the wrapped handler.
I am guessing most probably incorrectly that the first argument to a member function will be a pointer to the class - I did no research on the topic and I do not know if this is true. After getting the symbol name, we can write the main program:
// main.cpp
#include "a.hpp"
extern "C" {
void __real__ZN1A4testEi(A *t, int a);
void __wrap__ZN1A4testEi(A *t, int a) {
printf("wrap ");
return __real__ZN1A4testEi(t, a);
}
};
int main() {
A().test(5);
}
and compile&run with:
$ g++ -c -o a.o a.cpp
$ g++ -c -o main.o main.cpp
$ g++ -Wl,--wrap=_ZN1A4testEi a.o main.o -o main.out
$ ./main.out
wrap 5

g++ does not link depending on optimization settings

So, this is eating me for the last two days:
I can't link my application, depending on which compiler I use and what optimizations levels are used:
gcc plays nicely with -O1, -O2, -O3 but fails with -O0 and
clang plays nicely with -O2 and -O3 and fails with -O1 and -O0.
There are a bunch of horrible templates in it, but I see no reason for this obscure behaviour.
Here is a minimal amount of code that reproduces the problem:
#include <map>
#include <memory>
#include <iostream>
using id_type = long;
class CB : public std::enable_shared_from_this<CB>
{
public:
CB() = default;
virtual ~CB() = default;
};
template<class F, class C> class SC : public CB
{
};
class FB
{
public:
virtual ~FB() = default;
template<class T, class B> T* as(B* v) const { return dynamic_cast<T*>(v);}
};
template<class T>
class F : public FB
{
public:
virtual std::shared_ptr<CB> create() const
{
auto n = std::make_shared<T>();
return n;
}
};
struct B
{
virtual ~B() = default;
static const id_type ID = 1;
};
class A : virtual public B, virtual public SC<A, B>
{
public:
A() = default;
};
static std::map<id_type, std::shared_ptr<FB>> crtrs {
{A::ID, std::make_shared<F<A>>()}
};
int main()
{
std::cout << crtrs.size();
}
Here is the same online https://gcc.godbolt.org/z/sb9b5E
And here are the error messages:
fld#flap ~/work/p/test1
> $ g++ -O1 main.cpp
fld#flap ~/work/p/test1
> $ g++ -O2 main.cpp
fld#flap ~/work/p/test1
> $ g++ -O3 main.cpp
fld#flap ~/work/p/test1
> $ g++ -O4 main.cpp
fld#flap ~/work/p/test1
> $ g++ -O0 main.cpp
/tmp/cc8D7sNK.o: In function `__static_initialization_and_destruction_0(int, int)':
main.cpp:(.text+0x1c0): undefined reference to `B::ID'
collect2: error: ld returned 1 exit status
fld#flap ~/work/p/test1
> $ clang++ -O0 main.cpp
/tmp/main-c49b32.o: In function `__cxx_global_var_init.1':
main.cpp:(.text.startup+0x7a): undefined reference to `B::ID'
clang-8: error: linker command failed with exit code 1 (use -v to see invocation)
fld#flap ~/work/p/test1
> $ clang++ -O1 main.cpp
/tmp/main-cf18ee.o: In function `__cxx_global_var_init.1':
main.cpp:(.text.startup+0x3c): undefined reference to `B::ID'
clang-8: error: linker command failed with exit code 1 (use -v to see invocation)
fld#flap ~/work/p/test1
> $ clang++ -O2 main.cpp
fld#flap ~/work/p/test1
> $ clang++ -O3 main.cpp
If someone has any ideas what might be the reason, any hints are more than welcome.
You did not provide a definition of B::ID anywhere. It just happens that with higher optimization all accesses happened to be elided by the compiler.
You need to add the definition of a static member at toplevel scope:
const id_type B::ID;
If a static const data member is only read, it does not need a separate definition because compile time constants are not considered ODR-used (One Definition Rule).
The reason that you need the definition at all is that the map constructor expects std::map::value_type in the initializer list which is std::pair<const Key, T>. The constructor that gets picked in this case is pair( const T1& x, const T2& y );
In order to call this constructor the address of A::ID which is B::ID gets taken, which constitutes an ODR-use even for a constant.
Because the pair constructor is almost trivial in this case it gets inlined at higher optimization and the only reference to &B::ID disappears because B::ID's value is known and the pair can be directly initialized.
See also: static
If you are using C++17 or newer, you can also make B:ID constexpr instead of const, then you do not need a separate definition, because constexpr is implicitly inline (inline static const should also be OK).

How to force the linker to resolve weak symbols locally?

Using g++, when two compilation units "a1.o" and "a2.o" both define and use the same weak symbol, the linker will silently resolve to the first occurrence of the symbol wherever it is used. As a result, the behavior of the application will depend on the order of the object files on the linker command line. What can be done to ensure that these symbols are resolved locally to each compilation unit?
For instance, as a minimalist example, if I have the following source files:
a1.cpp:
#include <iostream>
struct A
{
void foo() {std::cerr << __FILE__ << std::endl;}
};
void bar1() {A a; a.foo();}
a2.cpp:
#include <iostream>
struct A
{
void foo() {std::cerr << __FILE__ << std::endl;}
};
void bar2() {A a; a.foo();}
main.cpp:
void bar1();
void bar2();
int main()
{
bar1();
bar2();
}
and compile them with:
for i in a1 a2 main ; do g++ -c -o $i.o $i.cpp ; done
The output will depend on the relative position of a1.o and a2.o on the linker command line:
g++ -o main main.o a{1,2}.o ; ./main
a1.cpp
a1.cpp
g++ -o main main.o a{2,1}.o ; ./main
a2.cpp
a2.cpp
I'd like to get the same result as if using the '-fno-weak' command line option:
for i in a1 a2 main ; do g++ -fno-weak -c -o $i.o $i.cpp ; done
g++ -o main main.o a{1,2}.o ; ./main
a1.cpp
a2.cpp
but '-fno-weak' seems to lead to other complications. What are the alternatives (besides not inlining and fixing collisions)?
For those wondering what could be a typical use case: when writing mock components it is sometimes convenient to have header-only implementations. Different test fixtures end-up having different mock implementations of the same component type, which becomes an issue when all the fixtures are linked into a single test runner.
You asked:
What are the alternatives (besides not inlining and fixing collisions)?
Use local namespaces or anonymous namespaces.
a1.cpp:
#include <iostream>
namespace A1_namespace
{
struct A
{
void foo() {std::cerr << __FILE__ << std::endl;}
};
}
using namespace A1_namespace;
void bar1() {A a; a.foo();}
or
#include <iostream>
namespace
{
struct A
{
void foo() {std::cerr << __FILE__ << std::endl;}
};
}
void bar1() {A a; a.foo();}
Make similar changes to a2.cpp.

Undefined symbol on a template operator overloading function

I have this function declaration:
template<class T>
a::A& a::A::operator<<(T out) {
std::cout << out;
return (*this);
}
and this function definition:
namespace a {
...
class A {
...
template<class T> A& operator<<(T);
And I call it as:
a::A b;
b << 1;
and this is the Makefile:
app: main.o A.o
g++ main.o A.o -o app
main.o: main.cpp
g++ -c main.cpp
A.o: A.cpp
g++ -c A.cpp
and it gives me:
Undefined symbols: a::A& a::A::operator<< <int>(int)
why is that?
The function template will be turned into an actual function at compile time, once the type represented by T (that is, int in your case) is actually known. However, this is not the case before main.cpp is compiled. At the time when A.cpp is compiled, the template function is not instantiated into an actual function, therefore the object file generated doesn't include the binary version of the function.
There are two ways to solve this.
Include the function definition in your header file. That is, make
template<class T>
a::A& a::A::operator<<(T out) {
std::cout << out;
return (*this);
}
a part of the header file, and remove the function definition from the .cpp file.
The effect of this is that any .cpp file that includes this header will be able to use any instantiation of the template, i.e. for any value of T.
Alternatively, include an explicit template instantiation statement in A.cpp:
template a::A& a::A::operator<<(int out);
This will cause the compiler to actually instantiate the template when A.cpp is compiled, and to include the compiled function in the object file. Hence the linker can find it when linking main.o and A.o together, and all is fine. The disadvantage is that it will only work for the specific types (in this case, only int) that you provided explicit instantiations for.
Try changing your definition to:
template<class T>
a::A& a::A::operator<< <T> (T out) {
std::cout << out;
return (*this);
}
?
(Make sure it's in the header file)