Использование переменной из другого файла перед основным

У меня небольшие проблемы с пониманием того, почему мой код работает так, как он работает (или не работает так, как должен).

Я пытаюсь написать (на C++) интерфейс, который позволяет использовать некоторые функции, работающие с unordered_map, из стандартной библиотеки шаблонов в C. Однако я также хотел бы написать пространство имен, которое позволяет использовать их и в C++.

Я спрашиваю не о том, как это можно сделать по-другому, а о том, почему это работает так, как работает;

Скажем на время, что мне нужны только две функции: добавлять элементы и писать размер карты. Заголовок следующий:

//project.h
#ifdef __cplusplus
extern "C" {
#endif

void add(int, int);

void give_size();

#ifdef __cplusplus
}
#endif

Исходный код:

//project.cc
#include <unordered_map>
#include <iostream>
#include "project.h"

using namespace std;

unordered_map<int, int> my_map;

void add(int arg, int val) {
    my_map.insert ({{arg, val}});
}

void give_size() {
    cout << my_map.size() << endl;
}

Интерфейс для С++:

//cproject
namespace pro {
    #include "project.h"
}

и тест:

//test.cc
#include "cproject"
namespace {
    unsigned long test() {
        ::pro::add(1,2);
        ::pro::add(3,4);
        return 0;
    }
    unsigned long dummy = test();
}
int main() {
    ::pro::give_size();
    return 0;
}

И, для полноты, Makefile:

g++ -Wall -std=c++11 -c -o project.o project.cc
g++ -Wall -std=c++11 -c -o test.o test.cc
g++ test.o project.o -o test

Проблема, конечно, в том, что запуск test выводит 0 вместо 2, а это значит, что карта исчезает где-то раньше main test.

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

Буду признателен за любую помощь в этом вопросе.


person Jytug    schedule 11.11.2015    source источник
comment
Вы намеревались объявить dummy static unsigned long?   -  person Jared Dykstra    schedule 11.11.2015
comment
Мне любопытно: почему вы требуете связи C для add и give_size?   -  person jbruni    schedule 11.11.2015
comment
Попробуйте еще раз с -std=c++11, указанным в аргументе ссылки? Спецификация инициализации статических объектов изменилась в C++11.   -  person M.M    schedule 07.12.2015


Ответы (1)


Да, это плохо названный статический порядок инициализации. Плохое название, потому что стандарт C++ называет его «динамической инициализацией»; "статическая инициализация" - это нечто другое.

а это значит, что карта исчезает где-то перед основным тестом

Не совсем. Проблема в том, что вы используете карту до ее появления, добавляя к ней значения. Теперь случается, что для некоторых реализаций карты инициализированное нулем состояние (и это то, что делается со всеми глобальными переменными до запуска любых динамических инициализаторов) совпадает с тем, что делает конструктор по умолчанию. Таким образом, код в test выполняется первым и пытается что-то добавить на карту, а функция вставки карты работает просто отлично, создавая узлы, устанавливая внутренние указатели на узлы и т. д.

Затем запускается фактический конструктор карты по умолчанию, который сбрасывает эти указатели на нуль, пропуская и забывая все созданные вами узлы. Ваши предыдущие вставки отменены, и карта снова пуста.

Решения, представленные в вашей ссылке, будут работать; вы неявно вызываете объекты через свободные функции, даже если не делаете это явно. Нет никакого реального различия. Вы по-прежнему заменяете глобальный my_map в project.cc функцией, которая возвращает ссылку на статический уровень функции (или указатель, в зависимости от того, какое именно решение вы выберете). Разница лишь в том, что вы вызываете эту функцию не из test.cc, а из add и give_size.

Кстати, вся эта штука с глобальным состоянием, как правило, довольно подозрительна. Он не является потокобезопасным и затрудняет понимание того, что делает программа. Подумайте о том, чтобы вообще не делать этого таким образом.

person Sebastian Redl    schedule 11.11.2015
comment
Спасибо, это было очень полезно. Справедливости ради, это была не моя идея, я учусь на университетском курсе по С++, который часто имеет тенденцию давать нереальные проблемы и предлагать наименее удобные способы их решения ;-) - person Jytug; 11.11.2015
comment
@Jytug Вполне возможно, что вы можете столкнуться с таким кодом. Я просто надеюсь, что ты никогда этого не напишешь. - person Sebastian Redl; 12.11.2015