I have a classcalled Tracer in C++. In my Tracer.h file I have:
class Tracer{
public:
// not useful to the question.
private:
Logger *m_logger;
const char *m_who;
const char *m_fileName;
const void * m_theObject;
int m_lineNum;
int m_log_level;
}
And in Tracer.cpp I have:
Tracer::Tracer(
Logger *logger,
const void *theObject,
const char *who,
const char *file,
int line,
int log_level
) :
m_logger(logger),
m_who(who),
m_fileName(file),
m_lineNum(line),
m_theObject(theObject),
m_log_level(log_level)
{
// more implementation which is not useful to the problem.
The problem is, when I initialize m_theObject(theObject), I get the following warning:
/home/pribeiro/workspace/Tracer.h:80: warning: 'const void* Tracer::m_theObject'
/home/pribeiro/workspace/Tracer.cpp:27: warning: when initialized here
Tracer.h:80 is the declaration of m_theObject in the Tracer.h and Tracer.cpp:27 is the m_theObject(theObject line.
In my compilation level, warnings are treated like errors. Why is the compiler complaining about the initiliazation of a const void * pointer?
That's not the whole warning. It's actually warning that m_lineNum and m_theObject are being initialised in the wrong order (see Member fields, order of construction):
Tracer.h:80: warning: 'const void* Tracer::m_theObject' is initialized before 'int Tracer::m_lineNum'
Tracer.cpp:27: warning: when initialized here
The fix is to swap the order of initialisation, or the order of definition:
Tracer::Tracer(
Logger *logger,
const void *theObject,
const char *who,
const char *file,
int line,
int log_level
) :
m_logger(logger),
m_who(who),
m_fileName(file),
m_theObject(theObject), // here
m_lineNum(line), // and here
m_log_level(log_level)
{
// more implementation which is not useful to the problem.
Sometimes compilers give a warning when members are initialised in the member initialisation list in a different order than they are declared in the header. I have seen g++ do this before. If you reorder your initialisation list so that it is in the same order as the members appear in your header file you should see this warning disappear.
The issue here is that, as per the C++ Standard, member variables are always (ALWAYS) initialized in the order of declaration, regardless of the order you give in the constructor.
The compiler nicely warns you about your misfit, because possibly you introduce unintended dependency chains.
Example:
struct Foo {
int _1, _2, _3;
Foo() : _3(_2+_1), // Third initialization
_2(_1+_1), // Second initialization
_1(1) // First initialization
{}
};
#include <iostream>
int main () {
Foo f;
std::cout << f._1 << f._2 << f._3 << '\n';
}
Output:
123
But the order of member initializers may let the maintainer think he could do better:
struct Foo {
int _1, _2, _3;
Foo() : _3(3),
_2(_3-1),
_1(_2-1)
{}
};
#include <iostream>
int main () {
Foo f;
std::cout << f._1 << f._2 << f._3 << '\n';
}
One might again expect the output of 123 as it looks like _3 is initiliazed first in that constructor. But that's not true:
./teh_program
13451434436167553
./teh_program
134514344118742913
./teh_program
13451434495068033
The results are totally unpredictable, multiple runs give different results without even recompiling. And the compiler is to prevent accidents like this.
Related
I want to prevent implicit conversions from std::string to std::filesystem::path or boost::filesystem::path.
As far as I know there is no way to modify system headers to make constructors explicit.
So I thought that I'll create a overrides of my internal api functions accepting std::string instead of fileystem::path and call assert(false) inside.
But later I started wondering that the compiler should know if given function declaration is ever referenced, so hypothetically it could detect call to such override at compile time instead of runtime and warn or fail the compilation.
Is my thinking correct? Is there any way to prevent calls to such functions at compile time?
Best,
Piotr
You can declare but not define your conversion. Better yet, you can declare the function as deleted. If it is ever called, you will get an error at compile-time.
void f(std::filesystem::path p)
{
// ...
}
void f(std::string) = delete; // explanation
Toy example
#include <iostream>
struct path
{
path(std::string) {} // OOPS, not explicit!
};
void f(path p)
{
std::cout << "f(path)\n";
}
void f(std::string) = delete;
int main(int argc, char** argv)
{
std::string s = "/dev/null";
//f(s); // error: use of deleted function 'void f(std::__cxx11::string)'
f(path{s}); // OK
}
(demo)
Everything works in Visual Studio 2017, but I get linker errors in GCC (6.5.0).
Here is some sample code that isolates my problem:
#include <iostream>
struct Foo{
static constexpr const char* s[] = {"one","two","three"};
};
int main(){
std::cout << Foo::s[0] << std::endl; //this works in both compilers
const char* const* str_ptr = nullptr;
str_ptr = Foo::s; //LINKER ERROR in GCC; works in VS
std::cout << str_ptr[1] << std::endl; //works in VS
return 0;
}
In GCC, I get undefined reference to 'Foo::s'. I need the initialization of Foo::s to stay in the declaration of the struct, which is why I used constexpr. Is there a way to reference Foo::s dynamically, i.e. with a pointer?
More Background Info
I'll now explain why I want to do this. I am developing embedded software to control configuration of a device. The software loads config files that contain the name of a parameter and its value. The set of parameters being configured is determined at compile-time, but needs to be modular so that it's easy to add new parameters and expand them as development of the device continues. In other words, their definition needs to be in one single place in the code base.
My actual code base is thousands of lines and works in Visual Studio, but here is a boiled-down toy example:
#include <iostream>
#include <string>
#include <vector>
//A struct for an arbitrary parameter
struct Parameter {
std::string paramName;
int max_value;
int value;
const char* const* str_ptr = nullptr;
};
//Structure of parameters - MUST BE DEFINED IN ONE PLACE
struct Param_FavoriteIceCream {
static constexpr const char* n = "FavoriteIceCream";
enum { vanilla, chocolate, strawberry, NUM_MAX };
static constexpr const char* s[] = { "vanilla","chocolate","strawberry" };
};
struct Param_FavoriteFruit {
static constexpr const char* n = "FavoriteFruit";
enum { apple, banana, grape, mango, peach, NUM_MAX };
static constexpr const char* s[] = { "apple","banana","grape","mango","peach" };
};
int main() {
//Set of parameters - determined at compile-time
std::vector<Parameter> params;
params.resize(2);
//Configure these parameters objects - determined at compile-time
params[0].paramName = Param_FavoriteIceCream::n;
params[0].max_value = Param_FavoriteIceCream::NUM_MAX;
params[0].str_ptr = Param_FavoriteIceCream::s; //!!!! LINKER ERROR IN GCC !!!!!!
params[1].paramName = Param_FavoriteFruit::n;
params[1].max_value = Param_FavoriteFruit::NUM_MAX;
params[1].str_ptr = Param_FavoriteFruit::s; //!!!! LINKER ERROR IN GCC !!!!!!
//Set values by parsing files - determined at run-time
std::string param_string = "FavoriteFruit"; //this would be loaded from a file
std::string param_value = "grape"; //this would be loaded from a file
for (size_t i = 0; i < params.size(); i++) {
for (size_t j = 0; j < params[i].max_value; j++) {
if (params[i].paramName == param_string
&& params[i].str_ptr[j] == param_value) {
params[i].value = j;
break;
}
}
}
return 0;
}
As you can see, there are enums and string arrays involved and these need to match, so for maintenance purposes I need to keep these in the same place. Furthermore, since this code is already written and will be used in both Windows and Linux environments, the smaller the fix, the better. I would prefer not to have to re-write thousands of lines just to get it to compile in Linux. Thanks!
The program is valid in C++17. The program is not valid in C++14 or older standards. The default standard mode of GCC 6.5.0 is C++14.
To make the program conform to C++14, you must define the static member (in exactly one translation unit). Since C++17, the constexpr declaration is implicitly an inline variable definition and thus no separate definition is required.
Solution 1: Upgrade your compiler and use C++17 standard (or later if you're from the future), which has inline variables. Inline variables have been implemented since GCC 7.
Solution 2: Define the variable outside the class definition in exactly one translation unit (the initialisation remains in the declaration).
It looks like you are not using C++17, and pre-C++17 this is undefined behavior, and one that I really dislike. You do not have a definition for your s, but you are ODR-using it, which means that you need to have a definition.
To define this, you have to define it in .cpp file.
In C++17 that would be the valid code. I am not familiar with MSVC, so I am not sure why it works fine there - be it that it is compiled as C++17, or because it is just a different manifestation of undefined behavior.
For C++98, C++11, and C++14 you need to explicitly tell the compiler where the initialization of Foo::s lives (see below) and you are good to go.
struct Foo{
static const char* s[];
};
const char* Foo::s[] = {"one","two","three"};
And as explained in one of the comments your initialization is OK from C++17.
I was playing a bit with external variables and was wondering why I cannot access an external structure from outside functions?
Here's my code:
a.h
struct testing {
unsigned int val;
const char* str;
testing(unsigned int aVal, const char* aStr) : val(aVal), str(aStr){};
}
extern testing externalStruct;
a.c
#include "a.h"
testing externalStruct(10, "test");
test.c
#include <iostream>
#include "a.h"
unsigned int valCopy = externalStruct.val;
const char* strCopy = externalStruct.str;
int main()
{
std::cout<<"Direct val : "<<externalStruct.val<<std::endl; // Working, print 10
std::cout<<"Direct str : "<<externalStruct.str<<std::endl; // Working, print "test"
std::cout<<"Copy val : "<<valCopy<<std::endl; // Print 0 instead of 10
std::cout<<"Copy str : "<<strCopy<<std::endl; // Print nothing
return 0;
}
Any idea?
This problem is caused by the fact that order of initialization of static (global) variables is unknown. It even has a name static initialization order fiasco. This means that global variables from test.c translation unit were initialized before global variables from a.c transation unit.
The usual solution is to use function with static variable. When function is called then static variable (during first use) is initialized. With c++11 initialization of such static function local variables is thread safe.
Solution for your code could look as follows:
a.h
//...
testing& GetExternalStruct();
a.c
//...
testing& GetExternalStruct() {
static testing externalStruct(10, "test");
return externalStruct;
}
test.c
unsigned int valCopy = GetExternalStruct().val;
const char* strCopy = GetExternalStruct().str;
You are being tricked by the static initialization order fiasco - One of the shortcomings of C++.
Both
unsigned int valCopy = externalStruct.val;
const char* strCopy = externalStruct.str;
and
testing externalStruct(10, "test");
are (and need to be) actually called before main() is executed. Unfortunately, C++ has no language construct that allows you to express in what order the initialization should be done - this is more ore less randomly decided by the compiler - In your case, the first block is apparently executed before the second, leading to the fact that externalStruct has not been initialized yet when you copy values from there into valCopy and strCopy.
You can work around this language deficiency by wrapping the initializations into functions that return a statically initialized value - This gives you control over the order those initializations are done.
a.c:
testing &x() {
static testing *t = new testing(10, "test");
return *t;
}
test.c
...
valCopy = x().val;
strCopy = x().str;
...
The order of initialization of the global objects is not defined.
In this case I guess that valCopy and strCopy were initialized before the constructor of externalStruct was invoked (=> initialized with junk).
If you put the initialization of valCopy and strCopy into the main body I believe everything will work properly:
int main()
{
unsigned int valCopy = externalStruct.val;
const char* strCopy = externalStruct.str;
std::cout<<"Direct val : "<<externalStruct.val<<std::endl; // Working, print 10
std::cout<<"Direct str : "<<externalStruct.str<<std::endl; // Working, print "test"
std::cout<<"Copy val : "<<valCopy<<std::endl; // ??? did it work?
std::cout<<"Copy str : "<<strCopy<<std::endl; // ??? did it work?
return 0;
}
EDIT
More info is here "C++ global initialization order ignores dependencies?"
Suppose you have this:
struct Foo {
Foo(unsigned int x) : x(x) {}
unsigned int x;
};
int main() {
Foo f = Foo(-1); // how to get a compiler error here?
std::cout << f.x << std::endl;
}
Is it possible to prevent the implicit conversion?
The only way I could think of is to explicilty provide a constructor that takes an int and generates some kind of runtime error if the int is negative, but it would be nicer if I could get a compiler error for this.
I am almost sure, that there is a duplicate, but the closest I could find is this question which rather asks why the implicit conversion is allowed.
I am interested in both, C++11 and pre C++11 solutions, preferably one that would work in both.
Uniform initialization prevents narrowing.
It follows a (not working, as requested) example:
struct Foo {
explicit Foo(unsigned int x) : x(x) {}
unsigned int x;
};
int main() {
Foo f = Foo{-1};
std::cout << f.x << std::endl;
}
Simply get used to using the uniform initialization (Foo{-1} instead of Foo(-1)) wherever possible.
EDIT
As an alternative, as requested by the OP in the comments, a solution that works also with C++98 is to declare as private the constructors getting an int (long int, and so on).
No need actually to define them.
Please, note that = delete would be also a good solution, as suggested in another answer, but that one too is since C++11.
EDIT 2
I'd like to add one more solution, event though it's valid since C++11.
The idea is based on the suggestion of Voo (see the comments of Brian's response for further details), and uses SFINAE on constructor's arguments.
It follows a minimal, working example:
#include<type_traits>
struct S {
template<class T, typename = typename std::enable_if<std::is_unsigned<T>::value>::type>
S(T t) { }
};
int main() {
S s1{42u};
// S s2{42}; // this doesn't work
// S s3{-1}; // this doesn't work
}
You can force a compile error by deleting the undesired overload.
Foo(int x) = delete;
If you want to be warned on every occurrence of such code, and you're using GCC, use the -Wsign-conversion option.
foo.cc: In function ‘int main()’:
foo.cc:8:19: warning: negative integer implicitly converted to unsigned type [-Wsign-conversion]
Foo f = Foo(-1); // how to get a compiler error here?
^
If you want an error, use -Werror=sign-conversion.
When I define a function of a class, I call another function of the same class within it. But when I do not type the class name it gives segmentation fault. Check below.
Header file
class DomainSolver
{
int fnc1 (UserDefinedType & var);
int fnc2 (UserDefinedType & var);
};
C file
int DomainSolver::fnc2 (UserDefinedType & var)
{
return 0;
}
int DomainSolver::fnc1 (UserDefinedType & var)
{
// fnc2 (var); // does not work
DomainSolver::fnc2(var); // works
return 0;
}
Wild guess… since the code you presented does not have any issues…
The function being called is declared virtual in a base class, so even if the virtual keyword is not present in the declaration here it is virtual.
The function being called does not access any member of the object.
You are calling the function on an invalid pointer or reference (for example through a null pointer or on an object that has already been deleted.
If all those guesses are right, the use of the qualification inhibits the dynamic dispatch mechanism, avoiding the dereference of an invalid pointer to the vtable. The code is still wrong (due to the third point above), but it seems to work.
The solution is not to call a member function through an invalid pointer or reference.
Although as pointed out by Zac's reply, the functions as you present them are not properly formed, there shouldn't be a difference between calling the scoped version; if you are getting a segfault in one case and not the other it's possibly because of code elsewhere.
Here is an example that works just fine:
dsolver.hh
#ifndef DSOLVER_HH
#define DSOLVER_HH
class DomainSolver
{
public:
int fnc1 (int& var);
int fnc2 (int& var);
};
#endif
dsolver.cc
#include <iostream>
#include "dsolver.hh"
int DomainSolver::fnc1 (int& var)
{
std::cout << "fnc1\n";
fnc2( var );
var = -1;
return var;
}
int DomainSolver::fnc2 (int& var)
{
std::cout << "fnc2\n";
var = 100;
return var;
}
main.cc
#include <iostream>
#include "dsolver.hh"
int main()
{
DomainSolver my_dsolver;
int my_int = 5;
my_dsolver.fnc1(my_int);
return 0;
}
Assuming this is close to your actual code, you have undefined behavior in fnc1:
int DomainSolver::fnc1 (UserDefinedType & var)
{
// fnc2 (var); // does not work
DomainSolver::fnc2(var); // works
// missing return!!!
}
You declare it to return an int, but then never return anything (in either case). Both cases are UB, so anything they do is technically "valid", since your code is not.
This code should be:
int DomainSolver::fnc1 (UserDefinedType & var)
{
return fnc2 (var);
}
As a side note: This is a good example of why you should never ignore the warnings given by the compiler (as you should have received a warning with both versions).
EDIT
With your latest edit adding a return value to fnc1, you'll need to show more of your actual code as there is not enough there to properly diagnose the problem (with the return being there, there is nothing wrong with your shown code).