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).
Given the following code as test.cpp,
Building using clang++ -c test.cpp -o test.o -g; clang++ test.o -g -all_load, setting breakpoint at return a.getValue(); and attempting to p a.getValue() from lldb:
Running llvm 3.8.0 on unix - works perfectly
Running xcode or llvm 8.1.0 on OSX - I get the following error:
error: Couldn't lookup symbols:
__ZNK4Test7MyClassILi2ELi3EE8getValueEv
Two interesting facts:
If I remove the last template argument - all works well
If I build directly without going through the .o file (clang++ test.cpp) = all goes well
Anyone has a clue what is going on, and how can it be fixed?
namespace Test{
template<class T>
class BLA{
public:
T getBlaValue() const{return 3;}
};
template <int N1, int N2, template<class T>class Impl = BLA>
class MyClass {
private:
public:
__attribute__((used))
int getValue() const
{
return 3;
}
};
}
int main()
{
Test::MyClass<2, 3> a;
return a.getValue();
}
I have the following code:
class C {
private:
void *data;
public:
constexpr C(nullptr_t) : data(nullptr) { }
C(int i) : data(new int(i)) { }
};
I have created a constructor which takes nullptr_t, so that I can have code similar to the following:
C foo(2);
// ...
foo = nullptr;
Code similar to this has worked previously on MSVC, however this code fails to compile on GCC 5.3.1 (using -std=c++14) with on the closing bracket of C(nullptr_t) with error: function definition does not declare parameters. Even if i give the parameter a name (in this case _), I get error: expected ')' before '_'. This also fails if the constexpr keyword is removed.
Why am I unable to declare such a constructor, and what are any possible workarounds?
You must be a fan of "using namespace std", and you just got tripped up by it:
constexpr C(std::nullptr_t) : data(nullptr) { }
gcc 5.3.1 compiles this, at --std=c++14 conformance level:
[mrsam#octopus tmp]$ cat t.C
#include <iostream>
class C {
private:
void *data;
public:
constexpr C(std::nullptr_t) : data(nullptr) { }
C(int i) : data(new int(i)) { }
};
[mrsam#octopus tmp]$ g++ -g -c --std=c++14 -o t.o t.C
[mrsam#octopus tmp]$ g++ --version
g++ (GCC) 5.3.1 20160406 (Red Hat 5.3.1-6)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
I have implemented a task queue with priority from asio examples and a timer class which use this queue. Here is the code:
priority_task_queue.h
class handler_priority_queue
{
private:
class queued_handler{
private:
size_t _priority;
std::function<void()> _function;
public:
queued_handler(size_t p, std::function<void()> f): _priority(p), _function(f){}
friend bool operator<(const queued_handler& a, const queued_handler& b){
return a._priority < b._priority;
}
void operator()() {
_function();
}
};
std::priority_queue<queued_handler> _handlers;
public:
// A generic wrapper class for handlers to allow the invocation to be hooked.
template <typename Handler> class wrapped_handler
{
private:
handler_priority_queue& _queue;
size_t _priority;
Handler _handler;
public:
handler_priority_queue& queue() {return _queue;}
size_t priority() {return _priority;}
wrapped_handler(handler_priority_queue& q, size_t p, Handler h)
: _queue(q), _priority(p), _handler(h){}
template <typename ...Args>
void operator()(Args&&... args){
_handler(std::forward<Args>(args)...);
}
};
template <typename Handler> wrapped_handler<Handler> wrap(size_t priority, Handler handler){
return wrapped_handler<Handler>(*this, priority, handler);
}
void add(size_t priority, std::function<void()> function);
void execute_all();
void execute_one();
bool empty();
};
// Custom invocation hook for wrapped handlers.
template <typename Function, typename Handler>
void asio_handler_invoke(Function f, handler_priority_queue::wrapped_handler<Handler>* h){
h->queue().add(h->priority(), f);
std::cout<<"LLAMANDO AL INVOKE"<<std::endl; //BORRAR!!
}
class C_priority_task_queue{
private:
asio::io_service& _io;
handler_priority_queue _pri_queue;
public:
template <typename Handler> handler_priority_queue::wrapped_handler<Handler> wrap(int priority, Handler handler){
return _pri_queue.wrap(priority, handler);
}
explicit C_priority_task_queue(asio::io_service& io): _io(io){}
C_priority_task_queue(C_priority_task_queue const&) = delete;
C_priority_task_queue& operator =(C_priority_task_queue const&) = delete;
asio::io_service& io() {return _io;}
void run();
};
priority_task_queue.cpp
void handler_priority_queue::add(size_t priority, std::function<void()> function){
_handlers.push(queued_handler(priority, function));
}
void handler_priority_queue::execute_one(){
if(!_handlers.empty()){
queued_handler handler = _handlers.top();
handler();
_handlers.pop();
}
}
bool handler_priority_queue::empty(){
return _handlers.empty();
}
void C_priority_task_queue::run(){
while (_io.run_one())
{
_io.poll();
while(!_pri_queue.empty())
{
_io.poll();
_pri_queue.execute_one();
}
}
}
base_timer.h
class C_timer {
private:
asio::high_resolution_timer _timer;
uint8_t _timer_id;
C_priority_task_queue& _prio_queue;
void timer_handler_internal(const asio::error_code& e, uint8_t timer_id, const uint64_t sched_time);
virtual void timer_handler(const uint64_t sched_time)=0;
public:
size_t _priority;
explicit C_timer(C_priority_task_queue& prio_queue, size_t priority);
virtual ~C_timer();
void set_timer(uint64_t sched_time);
int cancel();
};
base_timer.cpp
C_timer::C_timer(C_priority_task_queue& prio_queue, size_t priority):
_timer(prio_queue.io()), _timer_id(0), _prio_queue(prio_queue), _priority(priority){}
C_timer::~C_timer(){}
void C_timer::set_timer(uint64_t sched_time){
++_timer_id;
_timer.expires_at(std::chrono::time_point<std::chrono::high_resolution_clock>(std::chrono::milliseconds(sched_time)));
_timer.async_wait(_prio_queue.wrap(_priority, std::bind(&C_timer::timer_handler_internal, this,
std::placeholders::_1/*error*/, _timer_id, sched_time)));
}
int C_timer::cancel(){
++_timer_id;
return _timer.cancel();
}
void C_timer::timer_handler_internal(const asio::error_code& e, uint8_t timer_id,
const uint64_t sched_time){
if(e==asio::error::operation_aborted || timer_id != _timer_id){
return;
}
timer_handler(sched_time);
}
test class
class C_timer_test: public C_timer{
private:
int _period;
virtual void timer_handler(const uint64_t sched_time) override{
std::cout<<"timer fired"<<std::endl;
uint64_t current_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
set_timer(current_time + _period);
}
public:
C_timer_test(C_priority_task_queue& prio_queue, int priority, int period):C_timer(prio_queue, priority), _periodo(period){}
virtual ~C_timer_test(){}
void run(uint64_t delay=0){
uint64_t time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
set_timer(time + delay);
}
};
The problem is if I execute this :
int main()
{
asio::io_service io;
C_priority_task_queue prio_queue(io);
asio::io_service::work w(io);
C_timer_test ti1(prio_queue, 0, 2000);
ti1.run();
prio_queue.run();
return 0;
}
I get a segmentation fault.
However, if I execute the following code it works fine:
int main()
{
asio::io_service io;
C_priority_task_queue prio_queue(io);
asio::high_resolution_timer _timer1(io);
asio::io_service::work w(io);
C_timer_test ti1(prio_queue, 0, 2000);
ti1.run();
prio_queue.run();
return 0;
}
The only diference between both piece of code is in the second main I have added the following line asio::high_resolution_timer _timer1(io); that I haven't use in any place.
Debugging the program I have found that the signal is raising in this line:
func_(&owner, this, ec, bytes_transferred); in file task_io_service_operation.hpp
I am using asio version 1.10.6.
Any suggestion that what could be happening?
The backtrace from gdb:
gdb ./main
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
This GDB was configured as "i686-linux-gnu".
(gdb) r
[libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x0805f0d4 in ?? ()
(gdb) backtrace
#0 0x0805f0d4 in ?? ()
#1 0x080529fb in asio::detail::task_io_service::do_run_one (this=0x805f030, lock=..., this_thread=..., ec=...) at /src/cpp/external_lib/asio/include/asio/detail/impl/task_io_service.ipp:371
#2 0x080526ce in asio::detail::task_io_service::run_one (this=0x805f030, ec=...) at /src/cpp/external_lib/asio/include/asio/detail/impl/task_io_service.ipp:169
#3 0x08052c68 in asio::io_service::run_one (this=0xbffff08c) at /src/cpp/external_lib/asio/include/asio/impl/io_service.ipp:71
#4 0x08051f32 in C_priority_task_queue::run (this=0xbffff094) at priority_task_queue.cpp:19
#5 0x08049ac3 in main () at main.cpp:46
And here there is the MakeFile:
TARGET=main
SOURCES= main.cpp base_timer.cpp priority_task_queue.cpp
SOURCE_DIR=.
INCLUDE_LIB= -L/src/cpp/libcore
INCLUDE_DIR=-I/src/cpp/external_lib/asio/include \
-I/src/cpp/libcore/include
INSTALL_DIR=.
LIB=-pthread
CXX=g++
CFLAGS=-Wall -fexceptions -fpermissive -std=c++11 -DASIO_STANDALONE
CFLAGS_DEBUG = -g3 -DDEBUG
OBJDIR_DEBUG=obj
BINDIR_DEBUG=.
OBJECTS_DEBUG:= $(addprefix $(OBJDIR_DEBUG)/,$(SOURCES:.cpp=.o))
all: debug
$(OBJDIR_DEBUG)/%.o: $(SOURCE_DIR)/%.cpp
#test -d $(OBJDIR_DEBUG) || mkdir -p $(OBJDIR_DEBUG)
$(CXX) $(CFLAGS) $(CFLAGS_DEBUG) $(INCLUDE_DIR) -c $< -o $#
debug: $(OBJECTS_DEBUG)
#test -d $(BINDIR_DEBUG) || mkdir -p $(BINDIR_DEBUG)
$(CXX) -o $(BINDIR_DEBUG)/$(TARGET) $^ $(INCLUDE_LIB) $(LIB)
UPDATE
I my investigation, I have found that if I define the base_timer members (basically asio::high_resolution_timer) initializations in the .h, the code runs ok, but if I do this in the .cpp, the code crash.
I mean,
explicit C_timer(C_priority_task_queue& prio_queue, size_t priority):
_timer(prio_queue.io()), _timer_id(0), _prio_queue(prio_queue), _priority(priority){}
in .h works, but
C_timer::C_timer(C_priority_task_queue& prio_queue, size_t priority):
_timer(prio_queue.io()), _timer_id(0), _prio_queue(prio_queue), _priority(priority){}
in .cpp fails
Except for "unnatural" juggling with chrono (timepoint or duration, pick one!) the code looks ok. I can't reproduce any failure (GCC, linux):
Live On Coliru
If adding random variables make problems appear/disappear, you should think of Undefined Behaviour, use static analysis, valgrind/purify/... and code scrutiny to find your culprit. Valgrind, ASAN and UBSAN ran clean on my PC
#sehe thanks for your effort. The diference is If I put every code into a single file, main.cpp, it runs, but if I separate into several files, the problem still there. In other hand, If I instanciate a high_resolution_timer object in the main.cpp, no matter the place (inside main(), in separate function that never is called, ...) it runs, but without it, a segmentation fault is raised.
That's great: you have found a potential source of the UB: look at the use of static variables or inline functions that are not ODR-safe. (Double check all translation units are using the same compiler flags).
Also, keep in mind UB is UNDEFINED, so like adding an unrelated _timer1 changes the apparent behaviour (without changing the source of UB) the same thing could make it appear to work.
The fact that it runs clean on my machine tells you that this would have to be a platform-specific source of UB
The problem was that I had been using the version 1.10.2 of the library and it seems there was a bug.
I have updated to the last version 1.10.6 and now it runs ok.
I have a function that is declared like:
template<typename T>
void MyFunction();
And a class:
template<typename T>
class MyClass
{
public:
typedef void (*Function_T)();
Function_T m_Func;
void DoSomething()
{
m_Func = &MyFunction<T>;
}
}
When I use the class, I undefined symbols error on MyFunction<T>.
If I change DoSomething to
void DoSomething()
{
m_Func = &MyFunction<T>;
return;
MyFunction<T>();
}
Everything works, but that looks like a workaround and will probably not work with optimization.
I cannot add
template void MyFunction<T>;
to the class because it says it cannot be in class. Is there any other way I can force instantiation of the function?
Edit:
I was able to write a test that fails, but in g++ it has a different message and actually a compiler error: http://ideone.com/RbMnh
Your code will work with optimization as well. Although, I don't know why simply m_Func = &MyFunction<T> doesn't work. GCC 4.3.4 compiles it fine. Which compiler you're using?
And you can also do this:
void DoSomething()
{
if ( false) MyFunction<T>();
m_Func = &MyFunction<T>;
return;
}
By the way, the function pointer type is incorrectly defined. It should be this:
typedef void (*Function_T)();
// ^^ put this!
Your code compiles fine for me using GCC, so I'm not sure if this solution solves your particular problem, but you can explicitly instantiate template functions like so:
// Template function defined:
template <typename T>
void MyFunction() {
// body
}
// Template function instantiated:
template void MyFunction<int>();
The issue can be either a compiler bug, or an error in the parts of the code that you are not showing. Try to build a minimal example that reproduces the problem, this is the minimal example that I have been able to produce, and compiles fine with both clang++ 2.8 and g++ 4.4/4.5:
drodriguez#drodriguez-desktop:/tmp$ cat test.cpp
#include <iostream>
template <typename T>
void function() {
}
template <typename T>
struct type {
typedef void (*Func)();
Func _f;
void f() {
_f = &function<T>;
}
};
int main() {
type<int> t;
t.f();
std::cout << t._f << std::endl;
}
drodriguez#drodriguez-desktop:/tmp$ /usr/bin/clang++ --version
clang version 2.8 (branches/release_28)
Target: x86_64-pc-linux-gnu
Thread model: posix
drodriguez#drodriguez-desktop:/tmp$ /usr/bin/clang++ -o test test.cpp && ./test
1
drodriguez#drodriguez-desktop:/tmp$ g++ --version
g++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
drodriguez#drodriguez-desktop:/tmp$ g++-4.4 -o test test.cpp && ./test
1