I tried to overload QDebug::operator<< for std::string. I know that we can debug (using qDebug()) std::string objects using its std::string::c_str() function but I want to avoid typing .c_str each time.
Here is my attempt
#include <QDebug>
#include <string>
inline const QDebug& operator<< (const QDebug& qDebugObj, const std::string& str) {
return qDebugObj << str.c_str();
}
int main()
{
std::string s = "4444555";
qDebug() << s;
}
This program produces segmentation fault. What is incorrect with this code?
Here is the stack:
#1 0x00000037c407a911 in malloc () from /lib64/libc.so.6
#2 0x00000037ca8bd09d in operator new(unsigned long) () from /usr/lib64/libstdc++.so.6
#3 0x00000037ca89c3c9 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) ()
from /usr/lib64/libstdc++.so.6
#4 0x00000037ca89cde5 in ?? () from /usr/lib64/libstdc++.so.6
#5 0x00000037ca89cf33 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) () from /usr/lib64/libstdc++.so.6
#6 0x00000000004012ca in operator<< (qDebugObj=..., str="4444555") at main.cpp:5
If you look at every overloaded output operator, you will see that none have a const qualifier. Which is your problem, you try to modify a constant object. Remove the const qualification of the qDebugObject and the return value.
You should have compiler warnings screaming about it, and if not then you need to enable more warnings (at least use -Wall when compiling with GCC/clang).
The actual problem, as answered by Mike Seymour in a comment, is that your overload will be called recursively until you get a stack overflow.
A way of bypassing that might be to convert the string to something else, like for example a QString:
return qDebugObj << QString::fromStdString(str);
In addition to your attempt to make an output stream const, you also failed to follow the instructions in the QT documentation
// with the fixed output operator
inline QDebug operator<<(QDebug dbg, const std::string& str)
{
dbg.nospace() << QString::fromStdString(str);
return dbg.space();
}
QT wants the output operator passed by copy (not by reference). There used to be a reason for that, but I cannot remember what it was.
Related
I have a strange segmentation fault I am unable to properly understand. I am creating a function object with std::bind, then assign this to a std::function object, this apparently results in a segfault.
std::function<ara::com::Future<SetShapeOutput>(const
messages::test::SetShapeParams&)> SetShape_callback_;
void set_SetShape_callback(const std::function<ara::com::Future<SetShapeOutput>(
const messages::test::SetShapeParams&)>& callback) {
SetShape_callback_ = callback;
}
[somewhere else]
algo_service_.GetshapeServiceSkeleton()->set_SetShape_callback(
std::bind(&ShapeServerAraBinding::on_call_serviceshapeService_methodSetShape,
this, std::placeholders::_1));
// definition
ara::com::Future<adaptiveautosarapplication::shapeServiceSkeleton::SetShapeOutput>
on_call_serviceshapeService_methodSetShape(
const messages::test::SetShapeParams& araSetShapeParams);
And the stacktrace from gdb showing the assignment causes a segfault:
#0 0x000055c45c268839 in std::swap<std::_Any_data> (__a=..., __b=...) at /usr/include/c++/6/bits/move.h:191
#1 0x000055c45c267781 in std::function<ara::com::Future<serviceInterfaces::test::shapeService::SetShapeOutput> (messages::test::SetShapeParams const&)>::swap(s
td::function<ara::com::Future<serviceInterfaces::test::shapeService::SetShapeOutput> (messages::test::SetShapeParams const&)>&) (this=0x7fffea5d6be0, __x=...)
at /usr/include/c++/6/functional:2016
#2 0x000055c45c263934 in std::function<ara::com::Future<serviceInterfaces::test::shapeService::SetShapeOutput> (messages::test::SetShapeParams const&)>::operat
or=(std::function<ara::com::Future<serviceInterfaces::test::shapeService::SetShapeOutput> (messages::test::SetShapeParams const&)> const&) (this=0x58, __x=...)
at /usr/include/c++/6/functional:1931
#3 0x000055c45c26009f in shapeServer::adaptiveautosarapplication::shapeServiceSkeleton::set_SetShape_callback(std::function<ara::com::Future<serviceInterfaces:
:test::shapeService::SetShapeOutput> (messages::test::SetShapeParams const&)> const&) (this=0x0, callback=...)
at /app/tests/eclipseProject/projects/shapeRPC/build/autogen/algos/shape/server/ara/include/shapeServer_service.h:40
#4 0x000055c45c260508 in shapeServer::ShapeServerAraBinding::Initialize (this=0x7fffea5d6dd0)
at /app/tests/eclipseProject/projects/shapeRPC/build/autogen/algos/shape/server/ara/include/shapeServerAraBinding.h:69
#5 0x000055c45c25854c in main (argc=1, argv=0x7fffea5d6fd8)
at /app/tests/eclipseProject/projects/shapeRPC/build/autogen/algos/shape/server/ara/src/shapeServerAraMain.cpp:108
The problem cause is shown in the following line:
#3 0x000055c45c26009f in shapeServer::adaptiveautosarapplication::shapeServiceSkeleton::set_SetShape_callback(std::function<ara::com::Future<serviceInterfaces:
:test::shapeService::SetShapeOutput> (messages::test::SetShapeParams const&)> const&) (this=0x0, callback=...)
Note shapeServiceSkeleton this=0x0.
I'm making a NodeJS addon using the Nan library, and I'm running into an issue where calling a callback (created on the javascript side and passed to the addon to be executed asyncronously) will cause a segfault - but only about once every 10 thousand or so runs.
There's quite a bit of complexity involved in how everything operates, but I'm hoping that someone will see something I missed or be able to figure out what's going on.
The C++ callback function is created from the javascript callback like this:
auto nodeFunc = val.As<v8::Function>();
auto nodeCb = std::make_shared<Nan::Callback>(nodeFunc);
auto callback = [nodeCb] (std::string err, std::string val) -> void {
Nan::HandleScope scope;
v8::Local<v8::Value> argv[2];
if (err.length() == 0) {
auto isolate = v8::Isolate::GetCurrent();
auto json = v8::JSON::Parse(isolate, Nan::New(val).ToLocalChecked());
auto object = json.ToLocalChecked();
argv[0] = Nan::Null();
argv[1] = object;
} else {
argv[0] = Nan::Error(err.c_str());
argv[1] = Nan::Null();
}
try {
nodeCb->Call(2, argv);
} catch (std::exception& ex) {
std::cout << ex.what() << std::endl;
Nan::ThrowReferenceError(ex.what());
}
};
After creation, it is passed to a separate thread which eventually sends the callback to the main libuv thread using uv_async_send() and executes it. This works fine the vast majority of the time, but will very rarely segfault on the nodeCb->Call(2, argv) line.
If anyone has any insight into what's happening here, I'd really appreciate it.
Also, here's the call stack from gdb in case that's any help:
#0 0x00000000009870e0 in v8::Function::Call(v8::Local<v8::Value>, int, v8::Local<v8::Value>*) ()
#1 0x00000000010a562c in node::MakeCallback(node::Environment*, v8::Local<v8::Value>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*) ()
#2 0x00000000010a5a98 in node::MakeCallback(v8::Isolate*, v8::Local<v8::Object>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*) ()
#3 0x00007ffff47b4b30 in Nan::Callback::Call_ (this=0x20c3500, isolate=0x1ded750, target=..., argc=2,
argv=0x7fffffffa430) at ../node_modules/nan/nan.h:1477
#4 0x00007ffff47b4a93 in Nan::Callback::Call (this=0x20c3500, argc=2, argv=0x7fffffffa430)
at ../node_modules/nan/nan.h:1443
#5 0x00007ffff47b194b in detail::info::__lambda1::operator() (__closure=0x1e40710, err="",
val="{\"index\":1,\"status\":1}") at ../node/utils.hpp:125
#6 0x00007ffff47b37f2 in std::_Function_handler<void(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >), detail::info::setElementValue(T&, v8::Local<v8::Value>, size_t) [with T = std::function<void(std::basic_string<char>, std::basic_string<char>)>; size_t = long unsigned int]::__lambda1>::_M_invoke(const std::_Any_data &, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >) (__functor=..., __args#0="", __args#1="")
at /usr/include/c++/4.8.2/functional:2071
#7 0x00007ffff44cd339 in std::function<void (std::string, std::string)>::operator()(std::string, std::string) const (this=0x1e29c80, __args#0="", __args#1="")
at /opt/rh/devtoolset-4/root/usr/include/c++/5.2.1/functional:2271
#8 0x00007ffff44e172c in std::_Bind<std::function<void (std::string, std::string)> (char const*, std::string)>::__call<void, , 0ul, 1ul>(std::tuple<>&&, std::_Index_tuple<0ul, 1ul>) (this=0x1e29c80,
__args=<unknown type in /usr/local/lib/libSCPlay.so, CU 0x0, DIE 0x83e21>)
at /opt/rh/devtoolset-4/root/usr/include/c++/5.2.1/functional:1074
#9 0x00007ffff44daec8 in std::_Bind<std::function<void (std::string, std::string)> (char const*, std::string)>::operator()<, void>() (this=0x1e29c80)
at /opt/rh/devtoolset-4/root/usr/include/c++/5.2.1/functional:1133
#10 0x00007ffff44d3b58 in std::_Function_handler<void (), std::_Bind<std::function<void (std::string, std::string)> (char const*, std::string)> >::_M_invoke(std::_Any_data const&) (__functor=...)
at /opt/rh/devtoolset-4/root/usr/include/c++/5.2.1/functional:1871
#11 0x00007ffff44fab0a in std::function<void ()>::operator()() const (this=0x7fffffffa650)
at /opt/rh/devtoolset-4/root/usr/include/c++/5.2.1/functional:2271
#12 0x00007ffff44f890c in DeviceThread::asyncListener (handle=0x1efb9f0)
at /home/scl37510/Projects/SCPlay2/lib/device_thread.cpp:124
#13 0x0000000001316b0b in uv__async_event (loop=0x1de7fe0 <default_loop_struct>, w=<optimized out>,
nevents=<optimized out>) at ../deps/uv/src/unix/async.c:98
#14 0x0000000001316be3 in uv__async_io (loop=0x1de7fe0 <default_loop_struct>,
w=0x1de81a8 <default_loop_struct+456>, events=<optimized out>) at ../deps/uv/src/unix/async.c:138
#15 0x00000000013271b0 in uv__io_poll (loop=loop#entry=0x1de7fe0 <default_loop_struct>, timeout=0)
at ../deps/uv/src/unix/linux-core.c:380
#16 0x00000000013176c6 in uv_run (loop=0x1de7fe0 <default_loop_struct>, mode=UV_RUN_ONCE)
at ../deps/uv/src/unix/core.c:354
#17 0x00000000010aabe0 in node::Start(int, char**) ()
#18 0x00007ffff6bf5b35 in __libc_start_main () from /lib64/libc.so.6
#19 0x00000000007b1f1d in _start ()
Edit: I've created a much smaller test program to see if I could pinpoint the source of the bug, and I've discovered that I can prevent it by changing the Callback shared_ptr to a regular pointer, deleting it immediately after the nodeCb->Call(2,argv) line.
Is there some semantic difference between the two that could cause this?
The usage of shared_ptr to wrap a Callback is suspicious:
std::make_shared<Nan::Callback>(nodeFunc);
I don't think V8 would like references being handled that way.
Nan::Callback itself contains a Persistent that it uses to store the function's value, so you need not worry about persistence.
...I just noticed your edit after I already wrote out this answer. The shared_ptr is potentially dangerous to mix with V8 internal handles and references.
If your intention is to store the callback and delete it immediately, maybe you could benefit by refactoring your code to use Nan::AsyncWorker instead.
I have a large code base that can use boost::any or boost::spirit::hold_any (depending on a macro definition).
hold_any seems to be compatible with boost::any (e.g. How to print boost::any to a stream? or Type erasure - Part IV) and faster (Why you shouldn’t use boost::any) but I'm experiencing several segmentation fault errors using hold_any (Boost v1.55 / 1.54 / 1.53).
This is a minimal working example that exhibits the same problem as the original code:
#include <iostream>
#include <string>
#include <vector>
#include <boost/spirit/home/support/detail/hold_any.hpp>
typedef boost::spirit::hold_any any;
typedef std::vector<any> vany;
int main()
{
vany data0, data1;
for (unsigned i(0); i < 1000; ++i)
{
std::string s("test_test_test");
data0.push_back(any(s));
}
const unsigned n(data0.size());
vany::iterator iter(data0.begin());
for (unsigned i(0); i < n; ++i)
{
std::cout << "Moving " << i << std::endl;
data1.push_back(*iter);
iter = data0.erase(iter);
}
return 0;
}
The program appears to work correctly:
changing from boost::spirit::hold_any to boost::any;
changing the content of the hold_any to a data type small enough to perform small buffer optimization (e.g. from std::string to int).
It seems strange that there could be some major bug in a widely used library such as Boost Spirit, but
I'm having a hard time finding a bug in the example;
I've tried g++ / clang++ without success.
What's wrong with the example?
You should not be using hold_any as it is in detail/hold_any.hpp for a reason.
That said, hold_any's copy-assignment appears to be broken. I've created a pull request on github with a proposed fix.
Without the fix, the following program demonstrates UB (because the compiler generates a shallow assignment operator which is preferred):
#include <iostream>
#include <string>
#include <boost/spirit/home/support/detail/hold_any.hpp>
typedef boost::spirit::hold_any any;
int main()
{
any b;
{
any a;
a = std::string("test_test_test");
b = a;
}
std::cout << "b: " << b << '\n';
}
When run under valgrind:
==11827== Invalid read of size 8
==11827== at 0x5E9D793: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std
==11827== by 0x4012FC: boost::spirit::detail::fxns<mpl_::bool_<true> >::type<std::string, char>::stream_out(std::ostream&, void* const*) (hold_any.hpp:113)
==11827== by 0x4010F5: std::basic_ostream<char, std::char_traits<char> >& boost::spirit::operator<< <char>(std::basic_ostream<char, std::char_traits<char> >&, boost::spirit::basic_hold_any<char> const&) (hold_any.hpp:368)
==11827== by 0x400FC9: main (test.cpp:17)
==11827== Address 0x8ac1650 is 0 bytes inside a block of size 39 free'd
==11827== at 0x4C2BADC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11827== by 0x5EC405E: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==11827== by 0x401204: boost::spirit::detail::fxns<mpl_::bool_<true> >::type<std::string, char>::static_delete(void**) (hold_any.hpp:89)
==11827== by 0x401328: boost::spirit::basic_hold_any<char>::~basic_hold_any() (hold_any.hpp:246)
==11827== by 0x4010B4: boost::spirit::basic_hold_any<char>::~basic_hold_any() (hold_any.hpp:245)
==11827== by 0x400FA0: main (test.cpp:15)
I recently read about using GCC's code generation features (specifically, the -finstrument-functions compiler flag) to easily add instrumentation to my programs. I thought it sounded really cool and went to try it out on a previous C++ project. After several revisions of my patch, I found that any time I tried to use an STL container or print to stdout using C++ stream I/O, my program would immediately crash with a segfault. My first idea was to maintain a std::list of Event structs
typedef struct
{
unsigned char event_code;
intptr_t func_addr;
intptr_t caller_addr;
pthread_t thread_id;
timespec ts;
}Event;
list<Event> events;
which would be written to a file when the program terminated. GDB told me that when I tried to add an Event to the list, calling events.push_back(ev) itself initiated an instrumentation call. This wasn't terrible surprising and made sense after I thought about it for a bit, so on to plan 2.
The example in the blog which got me involved in all this mess didn't do anything crazy, it simply wrote a string to a file using fprintf(). I didn't think there would be any harm in using C++'s stream-based I/O instead of the older (f)printf(), but that assumption proved to be wrong. This time, instead of a nearly-infinite death spiral, GDB reported a fairly normal-looking descent into the standard library... followed by a segfault.
A Short Example
#include <list>
#include <iostream>
#include <stdio.h>
using namespace std;
extern "C" __attribute__ ((no_instrument_function)) void __cyg_profile_func_enter(void*, void*);
list<string> text;
extern "C" void __cyg_profile_func_enter(void* /* unused */, void* /* unused */)
{
// Method 1
text.push_back("NOPE");
// Method 2
cout << "This explodes" << endl;
// Method 3
printf("This works!");
}
Sample GDB Backtrace
Method 1
#0 _int_malloc (av=0x7ffff7380720, bytes=29) at malloc.c:3570
#1 0x00007ffff704ca45 in __GI___libc_malloc (bytes=29) at malloc.c:2924
#2 0x00007ffff7652ded in operator new(unsigned long) ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007ffff763ba89 in std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007ffff763d495 in char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00007ffff763d5e3 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6 0x00000000004028c1 in __cyg_profile_func_enter () at src/instrumentation.cpp:82
#7 0x0000000000402c6f in std::move<std::string&> (__t=...) at /usr/include/c++/4.6/bits/move.h:82
#8 0x0000000000402af5 in std::list<std::string, std::allocator<std::string> >::push_back(std::string&&) (this=0x6055c0, __x=...) at /usr/include/c++/4.6/bits/stl_list.h:993
#9 0x00000000004028d2 in __cyg_profile_func_enter () at src/instrumentation.cpp:82
#10 0x0000000000402c6f in std::move<std::string&> (__t=...) at /usr/include/c++/4.6/bits/move.h:82
#11 0x0000000000402af5 in std::list<std::string, std::allocator<std::string> >::push_back(std::string&&) (this=0x6055c0, __x=...) at /usr/include/c++/4.6/bits/stl_list.h:993
#12 0x00000000004028d2 in __cyg_profile_func_enter () at src/instrumentation.cpp:82
#13 0x0000000000402c6f in std::move<std::string&> (__t=...) at /usr/include/c++/4.6/bits/move.h:82
#14 0x0000000000402af5 in std::list<std::string, std::allocator<std::string> >::push_back(std::string&
...
Method 2
#0 0x00007ffff76307d1 in std::ostream::sentry::sentry(std::ostream&) ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#1 0x00007ffff7630ee9 in std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#2 0x00007ffff76312ef in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x000000000040251e in __cyg_profile_func_enter () at src/instrumentation.cpp:81
#4 0x000000000040216d in _GLOBAL__sub_I__ZN8GLWindow7attribsE () at src/glwindow.cpp:164
#5 0x0000000000402f2d in __libc_csu_init ()
#6 0x00007ffff6feb700 in __libc_start_main (main=0x402cac <main()>, argc=1, ubp_av=0x7fffffffe268,
init=0x402ed0 <__libc_csu_init>, fini=<optimized out>, rtld_fini=<optimized out>,
stack_end=0x7fffffffe258) at libc-start.c:185
#7 0x0000000000401589 in _start ()
Environment:
Ubuntu Linux 12.04 (x64)
GCC 4.6.3
Intel 3750K CPU
8GB RAM
The problem with using cout in the instrumentation function is that the instrumentation function is being called by __libc_csu_init() which is a very early part of the runtime's initialization - before global C++ objects get a chance to be constructed (in fact, I think __libc_csu_init() is responsible for kicking off those constructors - at least indirectly).
So cout hasn't had a chance to be constructed yet and trying to use it doesn't work very well...
And that may well be the problem you run into with trying to use std::List after fixing the infinite recursion (mentioned in Dave S' answer).
If you're willing to lose some instrumentation during initialization, you can do something like:
#include <iostream>
#include <stdio.h>
int initialization_complete = 0;
using namespace std;
extern "C" __attribute__ ((no_instrument_function)) void __cyg_profile_func_enter(void*, void*);
extern "C" void __cyg_profile_func_enter(void* /* unused */, void* /* unused */)
{
if (!initialization_complete) return;
// Method 2
cout << "This explodes" << endl;
// Method 3
printf("This works! ");
}
void foo()
{
cout << "foo()" << endl;
}
int main()
{
initialization_complete = 1;
foo();
}
The first case seems to be an infinite loop, resulting in stack overflow. This is probably because std::list is a template, and it's code is generated as part of the translation unit where you're using it. This causes it to get instrumented as well. So you call push_back, which calls the handler, which calls push_back, ...
The second, if I had to guess, might be similar, though it's harder to tell.
The solution is to compile the instrumentation functions separately, without the -finstrument-functions. Note, the example blog compiled the trace.c separately, without the option.
Or I have two files, each with one global initialization. One depends on the other.
Simplified example:
file1.h:
#include <string>
extern const std::string PREFIX;
file1.cpp:
#include "file1.h"
const std::string PREFIX = "prefix,";
file2.cpp:
#include "file1.h"
std::string MSG = PREFIX + "body";
int main(){}
I compile them as such:
/usr/local/bin/g++-4.6.2 -c -Wall -g -o file1.o file1.cpp
/usr/local/bin/g++-4.6.2 -c -Wall -g -o file2.o file2.cpp
/usr/local/bin/g++-4.6.2 -Wall -g -o example file1.o file2.o
When I run this, it segfaults. gdb trace:
Starting program: example
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b7ae0b in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&) ()
from /usr/local/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/libstdc++.so.6
(gdb) bt
#0 0x00007ffff7b7ae0b in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&) ()
from /usr/local/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/libstdc++.so.6
#1 0x00000000004009b5 in std::operator+<char, std::char_traits<char>, std::allocator<char> > (__lhs=Traceback (most recent call last):
File "/usr/local/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/../../../../share/gcc-4.6.2/python/libstdcxx/v6/printers.py", line 587, in to_string
return ptr.lazy_string (length = len)
RuntimeError: Cannot access memory at address 0xffffffffffffffe8
, __rhs=0x400af0 "body") at /usr/local/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/include/c++/bits/basic_string.h:2346
#2 0x000000000040095f in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535) at file2.cpp:3
#3 0x000000000040098b in _GLOBAL__sub_I_MSG () at file2.cpp:6
#4 0x0000000000400ab6 in __do_global_ctors_aux ()
#5 0x00000000004006e3 in _init ()
#6 0x00007ffff7ffa5a0 in ?? ()
#7 0x0000000000400a45 in __libc_csu_init ()
#8 0x00007ffff72dcbe0 in __libc_start_main () from /lib/libc.so.6
#9 0x00000000004007c9 in _start ()
Is there anyway to do what I'm trying to do (keeping in mind that this is an overly simplified example)? Or am I at the mercy of the initialization order chosen by the compiler/linker?
You need to make the prefix string be there before the other strings. One way is to change it to a C string
// header
#include <string>
extern const char * const PREFIX;
// .cpp file
const char * const PREFIX = "prefix,";
Another way is to return the prefix from a function and use PREFIX() where you previously used PREFIX.
// header
inline const string& PREFIX() {
static const string value = "prefix,";
return value;
}
At last, just a hint: Names with all uppercase letters are used for macros only in most coding conventions, so I would avoid such names for variables and functions.
I guess this appears because the code attempts to initialize your globals in the wrong order. There is a SO question about initialisation order, its answers should prove useful.
It's obviously not a portable solution, but for gcc you can use function attributes - with the __attribute__ syntax. i.e., the __init_priority__ (priority) attribute. The initialization priorities cross translation units. link