0

I have several independently-written translation units; for the sake of discussion, each of which has a (static or extern'ed) global std::string variable. These strings may have different names, unrelated to the names of the TUs. And I don't guarantee that the compiled TUs ever come in contact until they're linked with the TU having the main() function (say, main.cpp). They all include the same header, magic1.h, which defines a global variable named fun_strings, whose type we shall choose later.

Now, I want to be able to do the following:

#include <string>
#include "magic1.h"
int main() {
    // magic2
    for(const std::string& s : fun_strings) {
        foo(s);
    }
}

The point is that even though main.cpp _doesn't_include_any_of_the_other_TUs_, they have some static code which causes fun_strings to have copies/references/pointers to all of the TUs' individual strings.

The question is, of course, how to make this happen. I denoted "magic1" and "magic2" the places I might place common code in. And for an example TU with a string, take foo.cpp with std::string just_a_string("I'm a foo fun string"); somewhere in it.

Notes:

  • Solution must be thread-safe, although I'm guessing that should not be an issue.
  • This is a somewhat simplified version of what I'm actually trying to do, so apologies for the motivation being a bit obscure.
  • Ignore the potential for collisions, in the actual problem I'm having there can't be any.
  • I would rather not do anything based on dynamically loading the objects and going through their symbol tables (that's what I might be reduced to doing if I fail with a static approach)
einpoklum
  • 118,144
  • 57
  • 340
  • 684

1 Answers1

2

How about making a class that automatically adds the strings to fun_strings as they are declared:

class fun_string
{
public:
    fun_string(const char * string)
        : m_string(string)
    {
        get_fun_strings().push_back(m_string);
    }
private:
    string m_string;
};

In the translation units you will then define the global strings using fun_string.

In the above code I have used get_fun_strings() rather than a global named fun_strings directly. This is to avoid any static initialisation fiasco problems that may occur when using a static variable inside the initialisation of another static variable.

vector<string> & get_fun_strings()
{
    static vector<string> v;
    return v;
}

Note as well that as of C++11 local function statics are guaranteed to be initialised in a thread-safe manner. It's likely this isn't too much of an issue as, if you have at least one global fun_string, the function local static is going to be initialised before main() and hence likely before any additional threads have been created.

As for the ignoring collisions requirement -- this is something that could be done inside the fun_string constructor. That is to say you should check for a collision before the insertion and handle the situation appropriately.

Here is a live demo of this method.

A couple of caveats:

  • As mentioned by Yakk in the comments, do not expect the vector returned by get_fun_strings() to contain all the strings before main().
  • Do not use get_fun_strings() inside the destructors of static objects. See the page on static initialisation order for the reasons for this and how to alter it to support such usage.
user2093113
  • 3,230
  • 1
  • 14
  • 21
  • Note that prior to `main` the registrations may or may not have occurred. – Yakk - Adam Nevraumont Dec 12 '15 at 15:07
  • @Yakk: Are you sure? And if that's the case - doesn't that invalidate this suggested solution? – einpoklum Dec 12 '15 at 15:40
  • Note _prior to main_. I believe what @Yakk is warning you against is using the `get_fun_strings()` before the start of `main()` -- for example, in some other unrelated static initialisation code -- and expecting the `vector` to contain every string at that point. In my view this does not invalidate the solution if code that expects the `vector` to be 'complete' is only run after the start of `main()`. – user2093113 Dec 12 '15 at 16:47
  • @einpoklum after (or during) main, it will be registered. Prior to the start of main, some number may or may not be done. The order is implementation-determined and unpredictable. – Yakk - Adam Nevraumont Dec 12 '15 at 20:30
  • @Yakk: Ah, ok, that's fine. Obviously nobody guarantees the relative order of all this with other static initialization code. – einpoklum Dec 12 '15 at 21:36