Linker Error in template specialization of a class - c++

I have a header file TimeSeries.h, which has definitions and specializations of default_value template
TimeSeries class has other methods
I included TimeSeries.h file in main.cpp
This is my header file TimeSeries.h, followed by main.cpp
template<typename>
struct default_value;
template<>
struct default_value<int> {
static constexpr int value = 0;
};
template<>
struct default_value<double> {
static constexpr double value = std::numeric_limits<double>::quiet_NaN();
};
template <typename T>
class TimeSeries
{
public:
std::vector<uint64_t> timeUsSinceMid;
std::vector<T> values;
void addValue(uint64_t time, T value)
{
timeUsSinceMid.push_back(time);
values.push_back(value);
}
TimeSeries<T> * sample(uint64_t sampleFreq, uint64_t startTime=0, uint64_t
endTime=86400*1000*1000ULL)
{
//Some code
// I essentially faked a time and a default value push
TimeSeries<T>* newSample = new TimeSeries<T>;
newSample->timeUsSinceMid.push_back(timeUsSinceMid[0]);
newSample->values.push_back(default_value<T>::value);
return newSample;
}
};
Here is main.cpp:
#include<TimeSeries.h>
int main(int argc, const char * argv[]) {
TimeSeries<double> T;
T.addValue(1, 100.0);
T.addValue(2,200.0);
T.addValue(3,300.0);
T.addValue(4,400.0);
TimeSeries<double>* newT = T.sample(2,1,6);
//cout<<*newT<<endl;
return 0;
}
This is the linker error
Undefined symbols for architecture x86_64:
"default_value<double>::value", referenced from:
TimeSeries<double>::sample(unsigned long long, unsigned long long, unsigned long long) in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Can anyone please explain why "default_value::value" is undefined?

See the answers for this question.
Using the structure of your posted code
Your template definition defines the object value, but it still needs to be declared.
Don't ask me why, I'm just copying #Pete Becker's answer from the other post (which unfortunately wasn't very detailed). All I know is that the below code now compiles:
template<>
struct default_value<double> {
static constexpr double value = std::numeric_limits<double>::quiet_NaN();
};
// EDIT: inserted line below
constexpr double default_value<double>::value;
Changing the structure a bit
Alternatively, if you don't want to have to track value declarations through a large project, you can also turn the values into in-lined methods, like so: (edit added in constexpr's; also noting that inline is not required & likely doesn't change compiler behavior)
template<>
struct default_value<int> {
// EDIT: changed value to function
static inline constexpr int value() {
return 0;
}
};
template<>
struct default_value<double> {
// EDIT: changed value to function
static inline constexpr double value() {
return std::numeric_limits<double>::quiet_NaN();
}
};
Of course, remember to change your TimeSeries::sample method to use default_value<>::value as a method.

Related

C++ access static constexpr array

I am trying to get some values from an array which is declared in another class. The array has a fixed length and constant elements (I will 100% never modify its values, so that's why I made it constant).
However, when I try to access the first element in the main function, I get a compilation error:
basavyr#Roberts-MacBook-Pro src % g++ -std=c++11 main.cc
Undefined symbols for architecture x86_64:
"Vectors::vec1", referenced from:
_main in main-c29f22.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1
As you can see, I'm compiling on macOS Catalina using clang (latest version).
[Q]:What could be the issue?
Thank you in advance.
Here is the code:
#include <iostream>
class Dimension
{
public:
static constexpr int dim1 = 2;
static constexpr int dim2 = 1;
};
class Vectors
{
public:
static constexpr double vec1[2] = {4.20, 6.9};
};
int main()
{
auto a = Vectors::vec1[0]; //I also tried initializing this to a value rather than just accessing it directly through the class like I did below
std::cout << a << "\n";
std::cout << Vectors::vec1[0] << "\n";
return 0;
}
You're compiling in C++11 mode; you need to provide definition for these constexpr static members at namespace scope. Note that this is not required since c++17.
If a const non-inline (since C++17) static data member or a constexpr static data member (since C++11) is odr-used, a definition at namespace scope is still required, but it cannot have an initializer. This definition is deprecated for constexpr data members (since C++17).
e.g.
class Dimension
{
public:
static constexpr int dim1 = 2;
static constexpr int dim2 = 1;
};
constexpr int Dimension::dim1;
constexpr int Dimension::dim2;
class Vectors
{
public:
static constexpr double vec1[2] = {4.20, 6.9};
};
constexpr double Vectors::vec1[2];

How to fix "undefined symbol" in CLANG when using simple Template

I'm trying to implement a simple system using template struct, the code is very simple and compile fine with MSVC, yet i cannot understand why CLANG gives me this error: "lld-link : error : undefined symbol: public: static struct FMyStruct const TSpec<1>::m_struct"
I compile on a windows 64bitmachine with VisualStudio IDE but CLANG LLVM as compiler. The code works fine with MSVC.
I simplified my problem to the very minimum, i tried to put everything in one single cpp file, with no result. I also tried explicit template instanciation.
I want to be compliant with C++14, no C++17. One thing i tried that worked was declaring the m_struct member as an inline variable, but then i get this warning: "inline variables are a C++17 extension"
struct FMyStruct
{
const int _p0;
const int _p1;
const int _p2;
};
template< int > struct TSpec {
static constexpr FMyStruct m_struct = { 0, 0, 0 };
};
FMyStruct
Function( int i )
{
return TSpec< 1 >::m_struct;
}
int main()
{
return 0;
}
Result:
"lld-link : error : undefined symbol: public: static struct FMyStruct const TSpec<1>::m_struct"
I expect the linker to find the symbol m_struct since it is defined very next to it ...
The weirdest part is that if i try:
int
Function( int i )
{
return TSpec< 1 >::m_struct._p0;
}
the program will compile fine.
Edit: My CLANG version is 9.0.0, prebuilt distributed version for windows from the official website.
clang version 9.0.0 (trunk)
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files\LLVM\bin
It indeed seems to be a bug related to the CLANG version, thanks #Sombrero Chicken for pointing this out.
So this is definitely weird but i managed to solve this avoiding the C++17-specific 'inline' declaration of the static member by adding this after the template struct definition:
template< int N > const FMyStruct TSpec< N >::m_struct;
By the way, it does not seem to be related to the template declaration at all.
For summary, it gives this program that will compile fine.
struct FMyStruct
{
const int _p0;
const int _p1;
const int _p2;
};
template< int > struct TSpec {
static constexpr FMyStruct m_struct = { 0, 0, 0 };
};
template< int N > const FMyStruct TSpec< N >::m_struct;
FMyStruct
Function( int i )
{
return TSpec< 1 >::m_struct;
}
int main()
{
return 0;
}
I still do not really understand why this is necessary since the static member is public to the struct, and part of the same unit & file; i guess this is a different matter but i'd like to be enlightened. Thank you.

Link error with MSVC but not with g++ with constexpr

Consider the following code:
#include <iostream>
struct FactoryTag
{
static struct Shape {} shape;
static struct Color {} color;
};
template <typename TFactory>
int factoryProducer(TFactory tag)
{
if constexpr (std::is_same<TFactory, FactoryTag::Shape>::value)
return 12;
else if constexpr (std::is_same<TFactory, FactoryTag::Color>::value)
return 1337;
}
int main()
{
std::cout << factoryProducer(FactoryTag::shape) << std::endl;
return 0;
}
It works fine with g++ -std=c++1z Main.cpp but in Visual Studio with MSVC set with c++17 support it gives
Error LNK2001 unresolved external symbol "public: static struct FactoryTag::Shape FactoryTag::shape" (?shape#FactoryTag##2UShape#1#A) StaticTest C:\Users\danielj\source\repos\StaticTest\StaticTest\StaticTest.obj 1
Is this a bug in MSVC?
Is this a bug in MSVC?
No, FactoryTag::shape is odr-used here, so it needs a definition (you're copy-constructing it, which goes through the implicitly generated copy constructor, which requires you to bind a reference). Nor is this a bug in gcc either, arguably, since there is no diagnostic required if a definition is missing.
The solution is to add a definition. The old way would be:
struct FactoryTag { ... };
Shape FactoryTag::shape{}; // somewhere in a source file
The new way would be:
struct FactoryTag {
struct Shape {} static constexpr shape {}; // implicitly inline in C++17
};

c++ static class variable without cpp file

I have a simple class for storing sensor data which can be summarized as
class Data
{
public:
Data(){timestamp = Time::now(); id = sNextID++; data = 0; type = DATA_TYPE_UNKNOWN;}
double data;
Time timestamp;
DataType type;
private:
static unsigned int sNextID;
};
I have a header file that declares a bunch of similar data classes. Given the simplicity of the classes there is no need for an implementation cpp file.
The problem is, without an implementation file how do I initialize sNextID? I read somewhere that it defaults to 0 which would be fine, although relying on that seems a bit hackish. More importantly, though, without initializing it somewhere the linker complains of an undefined reference.
Use inline function (free-standing or member):
inline unsigned &sNextID()
{
static unsigned data = 0;
return data;
}
Or class template (defenition of it's statics can be in header file):
template<typename tag>
struct Foo
{
static unsigned sNextID;
};
template<typename tag>
unsigned Foo<tag>::sNextID=0;
Update: In C++17 inline variables are available:
struct Foo
{
static inline unsigned sNextID;
};
in the header file (let's say it's name is data.h), add this at the end
class Data
{
.....
private:
static unsigned int sNextID;
};
#ifdef MY_INIT
unsigned int sNextID = 0;
#endif
in the file where you have main
#define MY_INIT
#include "data.h"
in all other files where you are including the header, just a plain
#include "data.h"
This will ensure that the line unsigned int sNextID = 0; will be compiled into only one translation unit - the one with main.

Unresolved external error

I've written this beauty:
#include <iostream>
struct something {
static const char ref[];
};
const char something::ref[] = "";
template<int N, const char(&t_ref)[N], typename to> struct to_literal {
private:
static to hidden[N];
public:
to_literal()
: ref(hidden) {
for(int i = 0; i < N; i++)
hidden[i] = t_ref[i];
}
const to(&ref)[N];
};
template<int N, const char(&ref)[N], typename to> const to* make_literal() {
return to_literal<N, ref, to>().ref;
}
int main() {
const wchar_t* lit = make_literal<sizeof(something::ref), something::ref, wchar_t>();
}
It somewhat cleanly converts between string literal types. But when I compile it, MSVC says that the make_literal function is an undefined external function- which is clearly untrue as it's defined right there.
Edit: I've managed to reduce the problem down without all of the template gunk.
struct some {
friend int main();
private:
static wchar_t hidden[40];
public:
some()
{
}
};
int main() {
std::cout << some::hidden;
//const wchar_t* lit = make_literal<sizeof(something::ref), something::ref, wchar_t>();
}
main.obj : error LNK2001: unresolved external symbol "private: static wchar_t * some::hidden" (?hidden#some##0PA_WA)
It's just a static array. Does life hate me?
The issue is that is that to_literal::hidden is declared but never defined. Take another look:
struct something {
static const char ref[]; // declaration of something::ref
};
const char something::ref[] = ""; // definition of something::ref
template<int N, const char(&t_ref)[N], typename to> struct to_literal {
private:
static to hidden[N]; // declaration of to_literal::hidden (but there's no
// definition anywhere)
public:
to_literal()
: ref(hidden) {
for(int i = 0; i < N; i++)
hidden[i] = t_ref[i];
}
const to(&ref)[N];
};
To fix this, add a proper definition of to_literal::hidden:
template<int N, const char(&t_ref)[N], typename to>
to to_literal<N, t_ref, to>::hidden[N]; // definition of to_literal::hidden
When you define static members, a declaration does not suffice. You must provide a definition outside the class. I.e. add
wchar_t some::hidden[40];
outside the class, and it'll be defined.
Otherwise, if C++ allowed this, it'd cause the same problem as defining a global variable in a header -- every .cpp file that includes it will come with a duplicate definition, and at link time you'd get a multiply-defined symbol error.
You're declaring but not defining the static member. Add something like...
template<int N, const char(&t_ref)[N], typename to>
to to_literal<N, t_ref, to>::hidden[N];
I tried to check in MSVC for you too, but with VS2005 I get another stupid error...
template<int N, const char(&t_ref)[N], typename to>
to to_literal<N, t_ref, to>::hidden[N];
...compiler complains of...
error C3860: template argument list following class template name must list parameters in the order used in template parameter list
Looks like when they fix one bug, there's another one behind it ;-/.
When I built this with VC 2008, that wasn't the error I got. The error was:
Error 1 error LNK2001: unresolved
external symbol "private: static
wchar_t * to_literal<1,&public: static
char const * const
something::ref,wchar_t>::hidden"
(?hidden#?$to_literal#$00$1?ref#something##2QBDB_W##0PA_WA) main.obj Enabler
Removing static from the to hidden[N]; member resolved the issue.
Are you sure you got the error message correct?