static ints, compilation units and the ternary operator - c++

// SomeCls.h
class SomeCls
{
static const int PERIOD_ALARM_NORMAL = 5;
static const int PERIOD_ALARM_THRESH = 1;
void method()
{
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
} obj;
It is going to build ok. Now take out the method() implementation and place it in a cpp file:
//SomeCls.cpp
#include "SomeCls.h"
void SomeCls::method()
{
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
Why does mr. linker say
undefined reference to SomeCls::PERIOD_ALARM_NORMAL' undefined
reference toSomeCls::PERIOD_ALARM_THRESH'
?
Thanks
EDIT:
It seems to me that that inside .h, the ternary operator takes static const ints it as rvalues but ... outside the decalrative .h, it regards them as lvalue and needs definition.
This is what I have managed to understand from the answers below. Kudos to Bada compiler (some eabi linux thinggie)

If the compiler can't see all the static class constants' values, then you have to provide definitions for them so that they'll actually be stored somewhere. Add the following to your cpp file:
const int SomeCls::PERIOD_ALARM_NORMAL;
const int SomeCls::PERIOD_ALARM_THRESH;

This is a GCC limitation, but it's completely standard comforming. Technically a static const int is still an lvalue. You've provided the value inline so compiler will almost always use it as an rvalue. There is one exception. The abstract instructions emitted by the compiler for ternary operators queries the address of lvalues. Hence the error you're seeing.
You can work around this by using enum instead. Or if you're using a new version of GCC constexpr was added to standard to fix this exact problem (named and typed rvalues).
Alternatively you can provide the linker with a definition for the constants. E.g. in your classes cpp file add the a line like
// I wish I had constexpr
const int SomeCls::PERIOD_ALARM_NORMAL;
const int SomeCls::PERIOD_ALARM_THRESH;
As a side note: I was a staunch proponent of static const for class scope constants. Then I found out that MSVC doesn't allow for static const float with the value inline. So the only values you can portably put in a static const are integers, in which case enums provide all the same features plus the guarantee that they'll never silently convert to an lvalue.

If, for whatever reason, you compiler simply refuses to link the code (like GCC 4.4.5 does), here's a simple fix: Replace the static const ints with an enum.
// someclass.h
// include guards, blabla
class SomeClass
{
enum AlarmPeriod{
PERIOD_ALARM_NORMAL = 5,
PERIOD_ALARM_THRESH = 1
};
public:
void method();
};
// someclass.cpp
#include "someclass.h"
void SomeClass::method(){
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
// main.cpp
#include "someclass.h"
int main(){
someclass sc;
sc.method();
}
This links cleanly with GCC 4.4.5, which wouldn't link the former version, even though both are technically the same.
Note that you cannot, amongst other things, take the address of PERIOD_ALARM_NORMAL and PERIOD_ALARM_TRESH anymore, because both names are just aliases for their respective values.

Related

Hide define from external library to my code

To my current C++ project, I use an external library (1 big header) from a third party. This header provides multiple C like functions to drive hardware. To make it easier to use in C++, I wrote a C++ class to wrap those functions and hide this header with a pimpl implementation.
Some parameters for those functions are defined by preprocessor directives #define in their main header. I would like to use the value of those parameters outside my wrapper class but without include this header.
I tried to use forward declared enum in my C++ class. But members of my enum are not available outside the source file where they are defined
external_lib.h
#define PARAM_A_VAL_0 0
#define PARAM_A_VAL_1 1
bool external_function_param_a(int param_a);
wrapper.h
class wrapper
{
enum PARAM_A : int;
...
bool SetParamA(wrapper::PARAM_A a);
}
wrapper.cpp
#include <wrapper.h>
#include <external_lib.h>
enum wrapper::PARAM_A: int
{
VAL_0 = PARAM_A_VAL_0,
VAL_1 = PARAM_A_VAL_1
};
bool wrapper SetParamA(wrapper::PARAM_A a)
{
return external_function_param_a(a);
}
main.cpp
#include <wrapper.h>
int main()
{
wrapper w;
w.SetParamA(wrapper::PARAM_A::VAL_0);
// compilation error : VAL_0 not a member of wrapper::PARAM_A
}
Is there something wrong in my solution or this idea is just impossible? Is there a better solution. Create a lot of members to the class wrapper doesn't seem to be a good idea neither a switch on enum in all function members.
If you must keep compile-time const-ness, you would not be able to avoid inclusion of external header, as explained in this Q&A.
If compile-time const-ness is not a requirement, you could separate declarations and definitions of wrapper::PARAM_A::VAL_NNN constants, like this:
Header:
struct wrapper {
class PARAM_A {
int val;
PARAM_A(int val) : val(val) {}
friend class ::wrapper;
public:
static const PARAM_A VAL_0;
static const PARAM_A VAL_1;
};
bool SetParamA(wrapper::PARAM_A a);
};
Implementation:
const wrapper::PARAM_A wrapper::PARAM_A::VAL_0 = wrapper::PARAM_A(PARAM_A_VAL_0);
const wrapper::PARAM_A wrapper::PARAM_A::VAL_1 = wrapper::PARAM_A(PARAM_A_VAL_1);
bool wrapper::SetParamA(wrapper::PARAM_A a)
{
return external_function_param_a(a.val);
}
Now the use of the API remains the same as in your example:
wrapper w;
w.SetParamA(wrapper::PARAM_A::VAL_0);
w.SetParamA(wrapper::PARAM_A::VAL_1);
Demo.
Note the way the class PARAM_A hides the int value: since it is no longer an enum, direct use in place of int is no longer possible, so the call to external_function_param_a needs to "unwrap" the value.

Why the singleton initialization failed (link error) [duplicate]

Very simply put:
I have a class that consists mostly of static public members, so I can group similar functions together that still have to be called from other classes/functions.
Anyway, I have defined two static unsigned char variables in my class public scope, when I try to modify these values in the same class' constructor, I am getting an "unresolved external symbol" error at compilation.
class test
{
public:
static unsigned char X;
static unsigned char Y;
...
test();
};
test::test()
{
X = 1;
Y = 2;
}
I'm new to C++ so go easy on me. Why can't I do this?
If you are using C++ 17 you can just use the inline specifier (see https://stackoverflow.com/a/11711082/55721)
If using older versions of the C++ standard, you must add the definitions to match your declarations of X and Y
unsigned char test::X;
unsigned char test::Y;
somewhere. You might want to also initialize a static member
unsigned char test::X = 4;
and again, you do that in the definition (usually in a CXX file) not in the declaration (which is often in a .H file)
Static data members declarations in the class declaration are not definition of them.
To define them you should do this in the .CPP file to avoid duplicated symbols.
The only data you can declare and define is integral static constants.
(Values of enums can be used as constant values as well)
You might want to rewrite your code as:
class test {
public:
const static unsigned char X = 1;
const static unsigned char Y = 2;
...
test();
};
test::test() {
}
If you want to have ability to modify you static variables (in other words when it is inappropriate to declare them as const), you can separate you code between .H and .CPP in the following way:
.H :
class test {
public:
static unsigned char X;
static unsigned char Y;
...
test();
};
.CPP :
unsigned char test::X = 1;
unsigned char test::Y = 2;
test::test()
{
// constructor is empty.
// We don't initialize static data member here,
// because static data initialization will happen on every constructor call.
}
in my case, I declared one static variable in .h file, like
//myClass.h
class myClass
{
static int m_nMyVar;
static void myFunc();
}
and in myClass.cpp, I tried to use this m_nMyVar. It got LINK error like:
error LNK2001: unresolved external symbol "public: static class...
The link error related cpp file looks like:
//myClass.cpp
void myClass::myFunc()
{
myClass::m_nMyVar = 123; //I tried to use this m_nMyVar here and got link error
}
So I add below code on the top of myClass.cpp
//myClass.cpp
int myClass::m_nMyVar; //it seems redefine m_nMyVar, but it works well
void myClass::myFunc()
{
myClass::m_nMyVar = 123; //I tried to use this m_nMyVar here and got link error
}
then LNK2001 is gone.
Since this is the first SO thread that seemed to come up for me when searching for "unresolved externals with static const members" in general, I'll leave another hint to solve one problem with unresolved externals here:
For me, the thing that I forgot was to mark my class definition __declspec(dllexport), and when called from another class (outside that class's dll's boundaries), I of course got the my unresolved external error.
Still, easy to forget when you're changing an internal helper class to a one accessible from elsewhere, so if you're working in a dynamically linked project, you might as well check that, too.
When we declare a static variable in a class, it is shared by all the objects of that class. As static variables are initialized only once they are never initialized by a constructor. Instead, the static variable should be explicitly initialized outside the class only once using the scope resolution operator (::).
In the below example, static variable counter is a member of the class Demo. Note how it is initialized explicitly outside the class with the initial value = 0.
#include <iostream>
#include <string>
using namespace std;
class Demo{
int var;
static int counter;
public:
Demo(int var):var(var){
cout<<"Counter = "<<counter<<endl;
counter++;
}
};
int Demo::counter = 0; //static variable initialisation
int main()
{
Demo d(2), d1(10),d3(1);
}
Output:
Count = 0
Count = 1
Count = 2
In my case, I was using wrong linking.
It was managed c++ (cli) but with native exporting. I have added to linker -> input -> assembly link resource the dll of the library from which the function is exported. But native c++ linking requires .lib file to "see" implementations in cpp correctly, so for me helped to add the .lib file to linker -> input -> additional dependencies.
[Usually managed code does not use dll export and import, it uses references, but that was unique situation.]

"static const int" causes linking error (undefined-reference)

I am baffled by the linker error when using the following code:
// static_const.cpp -- complete code
#include <vector>
struct Elem {
static const int value = 0;
};
int main(int argc, char *argv[]) {
std::vector<Elem> v(1);
std::vector<Elem>::iterator it;
it = v.begin();
return it->value;
}
However, this fails when linking -- somehow it needs to have a symbol for the static const "value."
$ g++ static_const.cpp
/tmp/ccZTyfe7.o: In function `main':
static_const.cpp:(.text+0x8e): undefined reference to `Elem::value'
collect2: ld returned 1 exit status
BTW, this compiles fine with -O1 or better; but it still fails for more complicated cases. I am using gcc version 4.4.4 20100726 (Red Hat 4.4.4-13).
Any ideas what might be wrong with my code?
If you want to initialize it inside the struct, you can do it too:
struct Elem {
static const int value = 0;
};
const int Elem::value;
Try writing it as
struct Elem {
static const int value;
};
const int Elem::value = 0;
etc
.
static class members are generally supposed to be defined outside the class (declared inside, defined outside) in one compilation unit.
I don't remember how that interacts with inline initialization of const static integral members.
Also see this post: essentially, the problem is that somehow compiler ends up expanding your code into taking the address of Elem::value.
Why not just do this?
return Elem::value;
But the answer is that you are assigning a value in the declaration. This is supposed to work for basic types such as int, and is only required for complex types (i.e. classes, such as if you had a string instead of int). What I have found in practice is that this is hit or miss depending on what version of what compiler you are using. And, as you found out, which optimization level.
In most of the compilers defining in-class static const <integral type> works fine. But some compilers like Android NDK, such in-class definitions results in linker errors. For such case, we may use the typed enums:
struct X
{
enum : int64_t { VALUE = 100; }; // == static const int64_t VALUE = 100;
};

Static const variable is not constant in child class

I am using Visual Studio 2008 and have two classes Parent and Child. Parent declares some static const variables in the header, which are then defined in the cpp file. When I try to use the defines as cases in a switch statement in a child class I get the error: C2051: case expression not constant. So I've done some testing and the behavior I'm seeing is somewhat inconsistent.
// Parent.h
class Parent
{
public:
Parent();
~Parent(void) { }
static const unsigned long A = 1;
static const unsigned long B;
};
// Parent.cpp
#include "Parent.h"
const unsigned long Parent::B = 2;
Parent::Parent()
{
// Everything works fine here
unsigned long l;
switch(l)
{
case A:
break;
case B:
break;
default:
break;
}
}
// Child.h
#pragma once
#include "Parent.h"
class Child :
public Parent
{
public:
Child(void);
virtual ~Child(void) { }
static const int C = 3;
static const int D;
};
// Child.cpp
#include "Child.h"
const int Child::D = 4;
Child::Child(void)
{
unsigned long l;
switch(l)
{
case A:
break;
case B: // C2051: case expression not constant
break;
case C:
break;
case D:
break;
default:
break;
}
}
I've also tried specifying Parent::B directly, which doesn't solve the issue. Is there some reason why the expression is constant in all cases except when the variable is inherited from a parent class?
You can only use a static const integer-type member variable in a constant expression if
it is initialized with a constant expression and
that constant expression is visible at the time that it is used.
In your switch, the value of Parent::A is visible because its initializer is in the Parent.h header file. The same goes for Child::C. The value of Child::D is visible because its initializer occurs earlier in Child.cpp.
However, the value of Parent::B is not visible: C++ source files are compiled separately, so when compiling Child.cpp, the compiler knows that Parent::B is a static const integer-type member variable, but it doesn't know what its value is. Thus, it can't be used in a constant expression in Child.cpp.
Note that if you ever use Parent::A as an object (e.g., &Parent::A), you will still need to define B in Parent.cpp, using const unsigned long Parent::A;, without an initializer, since you put the initializer in the class definition.
I am surprised that Visual Studio let you get away with declaring the const outside of a class declaration. The line
static const unsigned long B;
inside your Parent class should not be allowed. When I tried your example on the Mac, which uses GNU g++ compiler, I got the following error:
error: declaration of 'const long unsigned int Parent::B' outside of class is not definition
As for why it works on one class, but not the other; my guess: inside the child.cpp file, the compiler saw that D was indeed declared as a const, but it has no knowledge of how B was defined (or redefined). To make this work, you should move all constant declarations to the class in the .h file and not .cpp file.
The reason is that for the compiler the static const is not constant, beacuse at compile time it does not yet have a value, which is needed to compile the case statement.
The value is added later at link-time, when parent.o is linked to child.o (Remember that with plugins or shared libs link time could be as late as runtime).

Unresolved external symbol on static class members

Very simply put:
I have a class that consists mostly of static public members, so I can group similar functions together that still have to be called from other classes/functions.
Anyway, I have defined two static unsigned char variables in my class public scope, when I try to modify these values in the same class' constructor, I am getting an "unresolved external symbol" error at compilation.
class test
{
public:
static unsigned char X;
static unsigned char Y;
...
test();
};
test::test()
{
X = 1;
Y = 2;
}
I'm new to C++ so go easy on me. Why can't I do this?
If you are using C++ 17 you can just use the inline specifier (see https://stackoverflow.com/a/11711082/55721)
If using older versions of the C++ standard, you must add the definitions to match your declarations of X and Y
unsigned char test::X;
unsigned char test::Y;
somewhere. You might want to also initialize a static member
unsigned char test::X = 4;
and again, you do that in the definition (usually in a CXX file) not in the declaration (which is often in a .H file)
Static data members declarations in the class declaration are not definition of them.
To define them you should do this in the .CPP file to avoid duplicated symbols.
The only data you can declare and define is integral static constants.
(Values of enums can be used as constant values as well)
You might want to rewrite your code as:
class test {
public:
const static unsigned char X = 1;
const static unsigned char Y = 2;
...
test();
};
test::test() {
}
If you want to have ability to modify you static variables (in other words when it is inappropriate to declare them as const), you can separate you code between .H and .CPP in the following way:
.H :
class test {
public:
static unsigned char X;
static unsigned char Y;
...
test();
};
.CPP :
unsigned char test::X = 1;
unsigned char test::Y = 2;
test::test()
{
// constructor is empty.
// We don't initialize static data member here,
// because static data initialization will happen on every constructor call.
}
in my case, I declared one static variable in .h file, like
//myClass.h
class myClass
{
static int m_nMyVar;
static void myFunc();
}
and in myClass.cpp, I tried to use this m_nMyVar. It got LINK error like:
error LNK2001: unresolved external symbol "public: static class...
The link error related cpp file looks like:
//myClass.cpp
void myClass::myFunc()
{
myClass::m_nMyVar = 123; //I tried to use this m_nMyVar here and got link error
}
So I add below code on the top of myClass.cpp
//myClass.cpp
int myClass::m_nMyVar; //it seems redefine m_nMyVar, but it works well
void myClass::myFunc()
{
myClass::m_nMyVar = 123; //I tried to use this m_nMyVar here and got link error
}
then LNK2001 is gone.
Since this is the first SO thread that seemed to come up for me when searching for "unresolved externals with static const members" in general, I'll leave another hint to solve one problem with unresolved externals here:
For me, the thing that I forgot was to mark my class definition __declspec(dllexport), and when called from another class (outside that class's dll's boundaries), I of course got the my unresolved external error.
Still, easy to forget when you're changing an internal helper class to a one accessible from elsewhere, so if you're working in a dynamically linked project, you might as well check that, too.
When we declare a static variable in a class, it is shared by all the objects of that class. As static variables are initialized only once they are never initialized by a constructor. Instead, the static variable should be explicitly initialized outside the class only once using the scope resolution operator (::).
In the below example, static variable counter is a member of the class Demo. Note how it is initialized explicitly outside the class with the initial value = 0.
#include <iostream>
#include <string>
using namespace std;
class Demo{
int var;
static int counter;
public:
Demo(int var):var(var){
cout<<"Counter = "<<counter<<endl;
counter++;
}
};
int Demo::counter = 0; //static variable initialisation
int main()
{
Demo d(2), d1(10),d3(1);
}
Output:
Count = 0
Count = 1
Count = 2
In my case, I was using wrong linking.
It was managed c++ (cli) but with native exporting. I have added to linker -> input -> assembly link resource the dll of the library from which the function is exported. But native c++ linking requires .lib file to "see" implementations in cpp correctly, so for me helped to add the .lib file to linker -> input -> additional dependencies.
[Usually managed code does not use dll export and import, it uses references, but that was unique situation.]