Как смягчить влияние API-интерфейса на общедоступные элементы в шаблонных классах? - PullRequest
0 голосов
/ 01 марта 2012

Допустим, у меня есть тип таблицы поиска, которую я могу построить для данного целого числа:

class FooLookupTable {
    ...
public:
    FooLookupTable(int radix) { 
        ...
    }
};

Тогда есть класс, параметр шаблона которого является тем же целым числом, и конструктор которого инициализирует экземпляр члена этой таблицы поиска:

template <int radix> class Foo {
    ...
private:
    FooLookupTable table;
public:
    Foo () : FooLookupTable (radix) {
        ...
    }
};

Во всем коде я создаю их с различными значениями radix:

 int main() {
     ...
     Foo<1> myFoo;
     Foo<1> yourFoo;
     Foo<10> theirFoo;
     ...
 }

Это работает и не создает проблем с потоками или API. Но он не делит таблицу радиусов на 1 между myFoo и yourFoo. Я мог бы жестко закодировать зависимость от предполагаемой библиотеки потоков и создать глобальную карту, заполненную по требованию. Но мой вопрос:

"В современном мире C ++ 11 есть ли чистый способ разработки библиотеки для Foo, у которой нет зависимостей вне стандартных библиотек?"

Я подумал об использовании статического члена для этого, поскольку каждое отдельное создание экземпляра класса шаблона создает только одну статическую переменную-член . Но это поднимает вопрос о том, кто отвечает за объявление пространства для статического члена и , кто бы это ни делал, должен «знать правильный способ его инициализации»:

 FooLookupTable Foo<1>::table (1);
 FooLookupTable Foo<10>::table (10);

 int main() {
     ...
     Foo<1> myFoo;
     Foo<1> yourFoo;
     Foo<10> theirFoo;
     ...
 }

Чтение того, что написано на эту тему, типа " C ++ Статическая инициализация члена (забавный шаблон внутри) * ", похоже, не вселяет особой надежды ... если только я что-то упустил Кроме того, что произойдет, если Foo сами экземпляры будут статичными? : - /

Ответы [ 2 ]

1 голос
/ 01 марта 2012

Во-первых, в C ++ 11 есть стандартная библиотека потоков, на которую можно положиться в соответствии со своим собственным определением.

Затем вы должны упомянуть, что вам нужно указать имя: это singleton.

Наконец, есть путь в C ++ 11.Язык знает о потоках, и поэтому статическая переменная инициализация безопасна для потоков :

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(...) {
     }
}

РЕДАКТИРОВАТЬ:

Вы упомянулинесколько справедливых моментов в комментариях.Вот ответ лучше для ваших нужд, я надеюсь.Фактически, тот факт, что FooLookup создает шаблонные foos, не помогает решить проблему.Это немного смутило меня, вам не нужны никакие шаблоны для этого.

// 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;
}

РЕДАКТИРОВАТЬ 2

Сделал поток функций безопасным.Виноват.Кажется, мое первое решение может быть лучше для вас в конце концов.Существует решение без <mutex>, если вы знаете (во время компиляции) список оснований, для которых вы хотите создать справочную таблицу.

1 голос
/ 01 марта 2012

статические члены шаблонов не будут хорошо работать в API, если вы хотите, чтобы все экземпляры Foo <1> использовали одну и ту же таблицу.Это происходит потому, что, переходя границы модуля, если модуль A создает Foo <1>, а модуль B создает Foo <1>, он будет дублировать статические члены.

Я думаю, что вам лучше всего сопоставить эти целые числа сих соответствующие таблицы во время выполнения.

Я мог бы жестко закодировать зависимость от предполагаемой библиотеки потоков и построить глобальную карту, которая заполняется по требованию.Но мой вопрос:

В современном мире C ++ 11 есть ли чистый способ разработки библиотеки для Foo, у которой нет зависимостей вне стандартных библиотек?

Как практический ответ, нет, пока нет.Вам нужны критические секции / взаимное исключение для вашей таблицы, если вы хотите обеспечить безопасность потоков (если вам не удастся создать общий контейнер, который является потокобезопасным и без блокировок без сторонних зависимостей), и в C ++ нет ничего нестандартного11, которая еще реализована (по крайней мере, в популярных компиляторах) для обеспечения кроссплатформенного параллелизма AFAIK.

В будущем функции параллелизма C ++ 11 будут поддерживать мьютексы и атомарные функции из коробки, но популярные компиляторы покапока не реализовал эту функцию, насколько мне известно.

...