Постепенно строить вектор указателей переменных во время компиляции

Я пытаюсь решить проблему, когда у меня есть несколько разрозненных объявлений переменных (в одной единице компиляции), и я хочу построить вектор их адресов во время компиляции (если это невозможно в качестве эффективного вектора пространства, другие структуры данных также будет работать, например, связанный список).

static type var1;
static type var2;
static type var3;

// ...
for (type *i : varlist)
   do something with each varX

В качестве расширенного примера и обоснования предположим, что у меня есть язык сценариев, и у меня есть способ изменять переменные сценария из C++, получая дескриптор, скажем, scriptvar *. Эти дескрипторы можно найти, вызвав функцию get_var с именем:

scriptvar *var1 = get_var ("namespace::var1");
scriptvar *var2 = get_var ("namespace::var2");

Это медленно, поэтому я хочу кэшировать значения scriptvar * в глобальных переменных. К сожалению, я должен вызывать get_var только после инициализации языка сценариев, что может произойти в конце программы, поэтому я не могу просто использовать get_var в выражении инициализатора, а должен отложить его вызов.

Было бы неплохо, если бы я мог написать класс scriptvarwrapper или использовать какие-то другие средства, которые позволили бы мне объявлять эти глобальные дескрипторные переменные в любой момент, а также построить эффективный по пространству вектор этих переменных во время компиляции, к которому я могу получить доступ позже:

struct scriptvarwrapper
{
  scriptvar *handle;
  const char *name;
  / ...
};

static scriptvarwrapper var1 ("namespace::var1");
static scriptvarwrapper var2 ("namespace::var2");
static scriptvarwrapper var3 ("namespace::var3");

void init_vars ()
{
  for (scriptvarwrapper *i : somecontainer)
     i->handle = get_var (i->name);
}

Было бы идеально, если бы этот контейнер оказался просто массивом/векторной структурой данных в памяти, состоящей из указателей на эти переменные. Очевидно, цель состоит в том, чтобы построить это во время компиляции, поэтому решения, помещающие что-то в std::vector в конструкторе, не решат проблему.

Обновление:

Чтобы прояснить проблему: мне нужно решение, которое автоматически компилирует массив или список всех этих объявленных переменных во время компиляции, без необходимости перечислять их по отдельности и без конструктора, динамически создающего список во время выполнения, предположительно с помощью какого-то классного и изящный метод метапрограммирования, такой как функция constexpr, которая каким-то образом связывает все эти переменные вместе в список или, что предпочтительнее, что-то, что приводит к массиву указателей на все объекты scriptvarwrapper в памяти, заканчивающиеся либо специальным значением, либо известным размером.

В частности, необходимость помещать объекты scriptvarwrapper в статический массив вручную не годится, равно как и помещения их в std::vector в их конструкторе недостаточно.

Обоснованием этого является ремонтопригодность — если я добавлю переменную в любом месте моей программы, я не хочу снова перечислять ее отдельно, потому что об этом легко забыть — и эффективность — я не хочу создавать динамическую структуру данных во время выполнения. для того, что фактически является константой, известной во время компиляции.

Предпочтение какого-либо массива связано с эффективностью - решение, которое приводит к 100-байтовой структуре для каждого такого объекта во время компиляции, конечно, не очень полезно, точно так же будет выделение очень большого массива только для будущего расширения.

Обновление 2:

Чтобы уточнить, точный синтаксис, макет и тип хранилища не так важны. Что важно, так это то, что у меня может быть несколько независимых и разбросанных по файлу (файлам) объявлений переменных, и их адреса автоматически и без ручного перечисления помещаются в какой-то контейнер только для чтения, который можно каким-то образом повторять в время выполнения, чтобы найти все эти объявления.

Цель состоит в том, чтобы эффективно найти все эти переменные, не забывая указать одну из них отдельно, а также не создавать динамические структуры данных во время выполнения, поскольку вся информация известна во время компиляции, а C++ — такой классный язык с компиляцией. метапрограммирование времени. Увы, я не знаю, как и возможно ли это вообще.

Обновление 3:

Извините за все эти обновления, я учусь, как трудно выразить эту проблему. Вот пример того, как все может выглядеть:

static scriptvarwrapper var1 ("name");
ADD_LIST (var1); // magic macro

static scriptvarwrapper var2 ("name");
ADD_LIST (var2);

Ключевым моментом здесь является то, что, хотя я должен перечислить каждую переменную и, возможно, даже использовать уродливый макрос, трудно не заметить или забыть перечислить переменную, потому что ADD_LIST находится непосредственно в месте объявления - помните, объявления могут быть разбросаны по всему длинному файлу или даже по некоторым включаемым файлам, поэтому я ищу решение, благодаря которому трудно забыть включить объявление в мой список.

Поэтому в идеале конструктор или простое действие по объявлению scriptvarwrapper должны убедиться, что оно указано в списке, поэтому его нельзя не заметить. Решение, которое помещает все в std::vector в конструкторе, будет работать, за исключением того, что оно будет выглядеть уродливым из-за накладных расходов во время выполнения.

Как старый специалист по C, которым я являюсь, я подумал об использовании расширений GCC, чтобы поместить их в свой собственный раздел ELF, так же, как сами конструкторы работают в системах ELF — они собираются по указателю в свой собственный раздел, и все такие разделы объединяются по ссылке время со специальным объектным файлом в конце, который предоставляет конечное значение дозорного.


person Remember Monica    schedule 18.04.2018    source источник
comment
можете ли вы кэшировать переменные с помощью get_var сразу после инициализации языка сценариев?   -  person Andrew Kashpur    schedule 18.04.2018
comment
может std::map поможет   -  person    schedule 18.04.2018
comment
@AndrewKashpur, если вы имеете в виду, остается ли дескриптор, возвращаемый get_var, действительным на неопределенный срок, то для этого примера ответ «да». Проблема в том, что get_var нельзя вызвать при инициализации программы, иначе его мог бы просто вызвать конструктор.   -  person Remember Monica    schedule 18.04.2018
comment
@user2176127 user2176127 std::map нельзя использовать во время компиляции, не так ли?   -  person Remember Monica    schedule 18.04.2018
comment
Ваша проблема может быть решена с помощью «отражения», но глупый С++ в настоящее время не предлагает эту функциональность.   -  person Andreas H.    schedule 19.04.2018


Ответы (1)


Я не совсем уверен, понимаю ли я ваш вопрос, но почему бы просто не использовать классические старые массивы:

static int var1;
static int var2;
static int var3;

static int* vars[] = { &var1, &var2, &var3, nullptr };

for(size_t i = 0; vars[i]; ++i)
    std::cout << *vars[i] << std::endl;

Это должно работать для всех типов данных, и это гарантированно произойдет во время компиляции.

Следующая версия также создается во время компиляции (по крайней мере, в Visual C++ 2017):

static const auto vararr = std::array<int*, 3>{ &var1, &var2, &var3 };

То же самое можно сделать для вас scriptvarwrapper:

struct scriptvarwrapper
{
    scriptvar *handle;
    const char *name;
};

static scriptvarwrapper vars[] = {
    {nullptr, "var1"},
    {nullptr, "var2"},
    {nullptr, nullptr}
};

void init_vars()
{
    for (size_t i = 0; vars[i].name; ++i)
        vars[i].handle = get_var(vars[i].name);
}

В C++ доступ к переменной сценария 'var1' можно получить с помощью vars[0].handle, а 'var2' находится в vars[1].handle.

Возможно, вы предпочтете следующее решение:

struct scriptvarwrapper
{
    scriptvar **handle;
    const char *name;
};

static scriptvar *var1 = nullptr;
static scriptvar *var2 = nullptr;

static scriptvarwrapper vars[] = {
    { &var1, "var1"},
    { &var2, "var2"},
    {nullptr, nullptr}
};

void init_vars()
{
    for (size_t i = 0; vars[i].name; ++i)
        *vars[i].handle = get_var(vars[i].name);
}

var1 и var2 как переменные сценария, инициализируются значением nullptr и добавляются в массив времени компиляции 'vars' с помощью 'scriptvarwrapper'. В «init_vars» переменные сценария инициализируются и затем могут использоваться путем доступа к «var1» и «var2» (даже не зная массива времени компиляции, используемого для их инициализации).

Простое в использовании решение, не требующее компиляции:

class ScriptVar
{
public:
    ScriptVar(const char *name_)
        : name(name_)
    {
        vars.insert(this);
    }

    scriptvar* operator->()
    {
        return handle;
    }

    static void initVars()
    {
        for (auto var : vars)
            var->handle = get_var(var->name);
    }

private:
    static std::set<ScriptVar*> vars;
    const char *name;
    scriptvar *handle;
};

const ScriptVar var1("namespace::var1");
const ScriptVar var2("namespace::var2");

Каждая определенная ScriptVar регистрируется в ScriptVar::vars, и после вызова ScriptVar::initVars() ко всем определенным ScriptVar можно получить доступ с помощью оператора ->.

person Andreas H.    schedule 18.04.2018
comment
Это хорошие альтернативы, но ни один из них не вычисляет список во время компиляции — первые несколько требуют, чтобы я выполнял работу вручную, последний — во время выполнения. Я постараюсь улучшить свой вопрос в надежде, что он станет яснее. - person Remember Monica; 18.04.2018
comment
Я думаю, что вам действительно нужен какой-то препроцессор, который предлагает некоторые функции отражения. Боюсь, это невозможно сделать с помощью стандартного C++, по крайней мере, до C++20 (см. meetingcpp.com/blog/items/) - person Andreas H.; 19.04.2018