Avoiding duplicate symbol due to initialization of specialization in header? - c++

I'm catching duplicate symbol errors due to definitions I am trying to provide in a header. Here's the error from the Minimal, Complete, and Verifiable example. The header files and source files are shown below.
$ clang++ main.cpp x.cpp y.cpp -o main.exe 2>&1 | c++filt
duplicate symbol Id<S>::id in:
/tmp/main-3f2415.o
/tmp/long long-d62c28.o
duplicate symbol Id<T>::id in:
/tmp/main-3f2415.o
/tmp/long long-d62c28.o
duplicate symbol Id<S>::id in:
/tmp/main-3f2415.o
/tmp/unsigned long long-bfa6de.o
duplicate symbol Id<T>::id in:
/tmp/main-3f2415.o
/tmp/unsigned long long-bfa6de.o
ld: 4 duplicate symbols for architecture x86_64
Here is a similar question, but it does not involve a specialization: Static member initialization in a class template. This question has the specialization but it is for MSVC and not Clang: How to initialize a static member of a parametrized-template class. And this question states to put it in the source (*.cpp) file but we are aiming for the header file to avoid Clang 3.8 and 'Id<S>::id' required here, but no definition is available warnings: Where should the definition of an explicit specialization of a class template be placed in C++?
GCC, ICC, MSVC, SunCC and XLC are OK with the initialization. Clang and LLVM is giving me the trouble. Clang and LLVM also has trouble with explicit instantiations of specializations and extern, so its it own special kind of hell.
We support C++03 though C++17, so we have to be careful of the solution. Naively I tried placing the initialization of the specializations in an unnamed namespace to keep the symbols from escaping the translation units, but it resulted in compile errors.
How do we avoid duplicate symbol definitions when initializing and specializing a template class in a header?
Below is the MCVE, which is a cat main.cpp a.h s.h s.cpp t.h t.cpp x.cpp y.cpp. The problem seems to be with a.h, which provides the specialization and initialization; and the source files x.cpp and y.cpp, which include a.h.
main.cpp
#include "a.h"
#include "s.h"
#include "t.h"
int main(int argc, char* argv[])
{
uint8_t s = Id<S>::id;
uint8_t t = Id<T>::id;
return 0;
}
a.h
#ifndef A_INCLUDED
#define A_INCLUDED
#include <stdint.h>
template <class T> class Id
{
public:
static const uint8_t id;
};
class S;
class T;
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
#endif
s.h
#ifndef S_INCLUDED
#define S_INCLUDED
class S {
public:
S();
};
#endif
s.cpp
#include "s.h"
S::S() {}
t.h
#ifndef T_INCLUDED
#define T_INCLUDED
class T {
public:
T();
};
#endif
t.cpp
#include "t.h"
T::T() {}
x.cpp
#include "a.h"
y.cpp
#include "a.h"

Clang/LLVM is not the problem. You're simply running into undefined behavior with a touch of no diagnostic required. The fix is simple. You need to put your specialization(s) in one translation unit. i.e,
a.cpp:
#include "a.h"
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
Then the command line:
clang++ main.cpp a.cpp x.cpp y.cpp -o main.exe 2>&1 | c++filt
And voila. It works.

You are violating the ODR (one definition rule) for Id::id and Id::id. They get defined for each translation unit they are included in and thus show up when you link.
Depending on your intent with the id's for class S and T, you have to give them a unique home. One might be to park them in main.cpp.
main.cpp add
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
Or, put the id of S in s.cpp and id of T in t.cpp:
s.cpp
#include "s.h"
#include "a.h"
template<> const uint8_t Id<S>::id = 0x01;
and the equivalent for t.cpp.
Don't forget to remove any traces of S and T in a.h.
But, if they are part of the a.h interface, the create an a.cpp and define them there.

You can use unnamed namespace which can make your class have internal linkage, e.g.
#ifndef A_INCLUDED
#define A_INCLUDED
#include <stdint.h>
namespace {
template <class T> class Id
{
public:
static const uint8_t id;
};
}
class S;
class T;
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
#endif
Replace this code with the contents in a.h in your example, then it will work because Id<T> in one translation unit is different from that in another translation unit due to internal linkage, thus one-definition rule is not violated.

Related

Accessing Global variable across files

I need to access m[] in c.cpp from d.cpp. The array m[] is a member of CD within a namespace and is declared in c.h within that class. So I declare a static array s[] in the same class CD in the file c.h. Instantiate it in c.cpp and use a function to copy the elements of the original array m[] to static arrays[]. Then, I #include "c.h" in d.cpp where I need to access it.
c.h
namespace k{
class CD {
string m[10];
static string s[10];
}}
c.cpp
#include "c.h"
namespace k{
string CD::s[10] = {"q"}
}
d.cpp
#include "c.h"
void func(){
string n = k::CD::s[0];
}
But I am getting an error saying undefined reference to k::CD::s.
Am I doing something wrong here? Or is there some other way of doing this?
Some notes:
c.h
#ifndef C_H_HEADER_GUARD // add header guard
#define C_H_HEADER_GUARD
#include <string> // you need this header
namespace k {
class CD {
// string is in the "std" namespace, so:
std::string m[10]; // note that this is private!
// "s" can't be private since you want to access it outside the class, in `func()`:
public:
static std::string s[10];
}; // class definitions need to end with ;
} // namespace k
#endif
c.cpp
#include "c.h" // enclose the header with quotation marks
namespace k {
using namespace std; // in here it's ok to use namespace std:
string CD::s[10] = {"q"}; // missing ;
} // namespace k
d.cpp
#include "c.h" // again, quotation marks
void func() {
std::string n = k::CD::s[0]; // missing std::
}
Now, this will compile the two translation units into c.o and d.o that you can use when linking with the TU containing main later.
g++ -c c.cpp d.cpp -Wall -Wextra -pedantic -pedantic-errors
Since d.cpp doesn't have a d.h declaring the function func though, you'll need to declare that where you want to use it. Not nice, but:
void func();

msvc inline static member variable redefinition

I'm new to C++17. Considering the following code:
// ---------------
// in MyClass.hpp
// ---------------
#pragma once
class MyClass {
public:
static const int A;
};
inline const int MyClass::A = 100;
// ---------------
// in test.cpp
// ---------------
#include <stdio.h>
#include "MyClass.hpp"
void test() {
printf("test: %p\n", &MyClass::A);
}
// ---------------
// in main.cpp
// ---------------
#include <stdio.h>
#include "MyClass.hpp"
extern void test();
int main() {
printf("main: %p\n", &MyClass::A);
test();
}
When compiled with MinGW-W64 g++ 8.1.0
g++ -std=c++17 main.cpp test.cpp -o test.exe
The output is
main: 00000000004044B0
test: 00000000004044B0
which works as expected.
However, in MSVC 2017
cl /std:c++17 main.cpp test.cpp
I got a compiler error, saying redefinition of "public: static int const MyClass::A". (Sorry, the compiler output contains Chinese characters. It's not appropriate to post here directly.)
Why the code works under g++, but fails in MSVC? Did I do something wrong?
I can confirm that Clang accepts your code without any warning.
What worries me is that cppreference shows the following note:
The inline specifier cannot re-declare a function or variable (since C++17) that was already defined in the translation unit as non-inline.
I could not really identify the real cause for that note in C++ standard. But as cppreference is generally correct in its warnings, I assume that it is the reason why MSVC chokes on your code. It would probably expect:
// ---------------
// in MyClass.hpp
// ---------------
#pragma once
class MyClass {
public:
static const int A = 100;
};
in order to avoid a former non inline declaration followed with an inline definition.

Multiple definition gets solved with templates

a.hpp:
#pragma once
struct S
{
static int v;
};
int S::v = 0;
b.hpp:
#pragma once
void addOne();
b.cpp:
#include "b.hpp"
#include "a.hpp"
void addOne()
{
S::v += 1;
}
main.cpp:
#include <iostream>
#include "a.hpp"
#include "b.hpp"
int main()
{
S::v = 2;
addOne();
S::v += 2;
std::cout << S::v << std::endl;
}
Does not work when compiling with g++ -std=c++14 main.cpp b.cpp && ./a.out (multiple definition of S::v).
However when I change the code to:
a.hpp:
#pragma once
struct S
{
template<typename T>
static int v;
};
template<typename T>
int S::v = 0;
and replace all S::v with S::v<void> it compiles and works how I intended the first example to work (outputs 5).
I believe I know why the first code example does not work: The int S::v = 0; line gets once compiled in the main.cpp unit and once in the b.cpp unit. When the linker links these two together, then the variable S::v gets essentially redefined.(?)
Why does the code with the template work?
Why does the code with the template work?
Essentially, because the standard says so.
With templates, the rules usually amoun to: "everyone using them must have their definition available." The exact same applies to static data members of class templates: a definition of such a static data member must be present in every translation unit in which it is odr-used. It's up to the compiler & linker to make sure this does not lead to errors.
Note that since C++17, you can solve the non-template case by making the static data member inline:
#pragma once
struct S
{
static inline int v = 0;
};

Multiple definition C++

Ok, this has been covered already, for example here: static array class variable "multiple definition" C++
But I am missing some details here.
I have got the following classes:
Foo.cpp
#include <iostream>
#include "Model.h"
int main(int argc, char** argv){
std::cout << "hello" << std::endl;
return 0;
}
Model.h
#ifndef MODEL_H
#define MODEL_H
#include <string>
#include "md2Loader.h"
class Model{
public:
Model();
Model(const std::string& model_file);
private:
md2_header_t header;
modelData_t model;
};
#endif
Model.cpp
#include "Model.h"
#include "md2Loader.h"
Model::Model(){}
Model::Model(const std::string& model_file){
model = md2Loader::load_model(model_file);
}
and md2Loader.h
#ifndef MD2LOADER_H
#define MD2LOADER_H
struct modelData_t{
int numVertices;
int numTextures;
// etc
};
struct md2_header_t {
std::string version;
};
class md2Loader{
public:
md2Loader(){};
static modelData_t load_model(const std::string& model_file);
};
modelData_t md2Loader::load_model(const std::string& model_file){
modelData_t result;
result.numVertices = 1000;
result.numTextures = 10;
return result;
}
#endif
The linker complains of multiple definitions. But I am not quite sure, why. Do the #ifndef, #define preprocessor directives not help? I kind of get it that md2Loader.h get included to both Model.cpp and Model.h. When I do the implementation in Model.h and leave Model.cpp away it will compile and link just fine. I thought, that the directives for the preprocessor prevent that from happening, but obviously I am missing something there.
You need to either inline the function defined in header file or move it into a source file. Because that function will be appeared in multiple translation unit.
inline modelData_t md2Loader::load_model(const std::string& model_file){
modelData_t result;
result.numVertices = 1000;
result.numTextures = 10;
return result;
}
See more on inline specifier
There may be more than one definition of an inline function or variable (since C++17) in the program as long as each definition appears in a different translation unit. For example, an inline function or an inline variable (since C++17) may be defined in a header file that is #include'd in multiple source files.

Static variable link error, C++

Consider this code.
//header.h
int x;
//otherSource.cpp
#include "header.h"
//main.cpp
#include "header.h"
...
int main()
{
}
In this case compiler erred with the message. "fatal error LNK1169: one or more multiply defined symbols found"
but when I add static before x, it compiles without errors.
And here is the second case.
//header.h
class A
{
public:
void f(){}
static int a;
};
int A::a = 0;
/otherSource.cpp
#include "header.h"
//main.cpp
#include "header.h"
...
int main()
{
}
In this case compiler again erred with multiple declaration.
Can anybody explain me the behavior we static variables in classes and in global declarations?? Thanks in advance.
The issue with the static member variable is that you have the definition occur in the header file. If you #include the file in multiple source files, you have multiple definitions of the static member variable.
To fix this, the header file should consist only of this:
#ifndef HEADER_H
#define HEADER_H
// In the header file
class A
{
public:
void f(){}
static int a;
};
#endif
The definition of the static variable a should be in one and only one module. The obvious place for this is in your main.cpp.
#include "header.h"
int A::a = 0; // defined here
int main()
{
}
Declare x as extern in header.h to tell the compiler that x will be defined somewhere else:
extern int x;
Then define x once in the source file which you think is most fitting.
For example in otherSource.cpp:
int x = some_initial_value;