How to mitigate user-facing API Effect of shared members in templated classes?

雨燕双飞 提交于 2019-11-29 18:19:33

static members of templates won't work well across APIs if you want to ensure all instantiations of Foo<1> share the same table. This is because, going across module boundaries, if module A creates Foo<1> and module B creates Foo<1>, it will duplicate the static members.

I think your best bet is to map those integers to their corresponding tables at runtime.

I could hardcode a dependency on an assumed thread library, and build a global map that's filled on-demand. But my question is:

In the modern C++11 world, is there a clean way designing a library for Foo which does not have dependencies outside of the standard libraries?

As a practical answer, no, not yet. You need critical sections/mutual exclusion for your table if you want thread safety (unless you can manage to make a shared container that is thread safe and lock-free with no third party dependencies) and there's nothing out of the box in C++11 that's implemented yet (at least in popular compilers) to give you cross-platform concurrency AFAIK.

In the future, C++11's concurrency features will support mutexes and atomics out of the box, but popular compilers haven't implemented this feature yet to my knowledge.

J.N.

First, there's a standard thread library in C++11 you can rely on according to your own definition.

Then, you should mention that what you need as name: it's a singleton.

Finally, there's a way in C++11. The language is aware of threads, and as such, static variable initialization is thread safe:

template <int radix>
class Foo {
public:
     // your public stuff, no contructors

     static Foo& GetInstance()
     {
          static Foo instance(...); // thread safe accoding to C++11
          return instance;
     }

private:
     Foo(...) {
     }
}

EDIT:

You have mentioned a few fair points in the comments. Here is an answer better for your needs, I hope. Actually, the fact that FooLookup create templated foos is not useful to solve the problem. It confused me a bit, you don't need any templates for this.

// Header file
class FooLookupTable
{
public:
    // needed to use with map
    FooLookupTable()
    { }
    FooLookupTable(int index)
    { }

    static FooLookupTable& GetLookup(int index);
};


// CPP file (no library boundary problem since this file will be linked only once)

// I fear I can't do that without using a mutex explicitely, but those are in the standards.
// You'll need a recent compiler to support them (GCC 4.5 and MSVC11)
#include <mutex> 
using namespace std;

FooLookupTable::FooLookupTable& GetLookup(int index);
{
    static map<int, FooLookupTable> _tables; // thread safe in C++11
    static mutex _m;

    // The function body isn't thread safe though, so we need to protect it.
    lock_guard<mutex> l(_m); // make sure we don't create the same one twice.

    auto it = _tables.find(index);
    if (it == _tables.end()) {
        _tables[index] = FooLookupTable(index);
        return _tables[index];
    }
    else
        return *it;
}

EDIT 2

Made the function thread safe. My bad. It seems my first solution may be better for you after all. There is a solution without <mutex> if you know (at compilation time) the list of the radixes for which you want to create a lookup table.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!