Может ли идиома «построить при первом использовании» потерпеть неудачу при любых обстоятельствах? - PullRequest
5 голосов
/ 06 сентября 2011

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

string& GetString() {
    static string strFilename;
    return strFilename;
}

void PrintToScreen() {
    printf("String: %s\n", GetString().c_str())
}

Затем в моем main.cpp (вне библиотеки) я делаю:

GetString() = "abc";
printf("String: %s\n", GetString().c_str());
PrintToScreen();

И я получаю этот вывод:

String: abc
String:

Так выглядит второй вызов функции (но сделано из другого файла, который находится внутри библиотеки) каким-то образом очистить предыдущее значение, повторно инициализировать его или использовать собственную копию.
Я изменил функцию GetString, чтобы использовать 'new', но результат точно такой же (кстати, программа никогда не падает).
Но я не понимаю, горячо это возможно?
Есть идеи, что я делаю не так?

------------------------------- ОБНОВЛЕНИЕ --------------- ---------------

  1. Испытание выполнено в однопоточной среде.
  2. Он работает на некоторых платформах, а на некоторых - нет (работает на Windows, MacOS и AIX, не работает на Linux, HP_UX, Solaris, FreeBSD ...)
  3. Я проверял адрес имени strFilename во время выполнения (printf внутри GetString) и похоже, что это одна переменная без дубликатов (адрес всегда один и тот же)
  4. НО, с nm на финальной lib я получаю что-то вроде этого:

0000000000000030 T _Z16GetLogprintfFilev
0000000000000008 b _ZGVZ16GetLogprintfFilevE16strLogprintfFile
0000000000000018 b _ZZ16GetLogprintfFilevE16strLogprintfFile
U _Z16GetLogprintfFilev

и с nm на моей базовой lib (используется final lib) я получаю:

0000000000000030 T _Z16GetLogprintfFilev
0000000000000008 b _ZGVZ16GetLogprintfFilevE16strLogprintfFile
0000000000000018 b _ZZ16GetLogprintfFilevE16strLogprintfFile

Ответы [ 2 ]

3 голосов
/ 07 сентября 2011

Да, это возможно при статическом связывании.

Пример:

 libA.a   // contains GetString()
          // Contains. PrintToScreen()
          // Here the reference has been resolved.

 libX.so  // Contains a call to GetString()
          // This library is linked with libA.a
          // Thus pulls in the function GetString() into this library.

 libY.so  // Contains a call to PrintToScreen()
          // This library is linked with libA.a
          // Thus pulls in the function PrintToScreen and GetString() into this library.

 a.out    // linked against libY.so libX.so
          // This has two distinct versions of GetString()

В приведенном выше примере, если a.out содержит вызов getString (), это зависит от ОС, какая версия getString () будет вызвана. В большинстве систем используется порядок загрузки отдельной совместно используемой библиотеки, но в других он сначала выполняет поиск совместно используемых библиотек (т.е. lib X загружает XA XB, а Y загружает YA YB. или XY XA XB YA YB). Вам необходимо обратиться к документации по каждой общей библиотеке ОС, чтобы понять, как искать символы во время выполнения.

Решение здесь состоит в том, чтобы связываться только с общими библиотеками (по умолчанию в большинстве ситуаций).
Таким образом, вы получаете только одну копию libA (при условии, что вы сделали libA общей библиотекой) и ее содержимое загружается в среду выполнения только один раз (без копий).

Примечание. Это не ошибка на уровне языка.
Это ошибка, вызванная связыванием, которая выходит за рамки языка C / C ++.

0 голосов
/ 07 сентября 2011

На самом деле в одном примере не хватало мысли. Это должно выглядеть так:

string& GetString() {
  static string strFilename;
  return strFilename;
}

extern "C" {
  void PrintToScreen() {
    printf("String: %s\n", GetString().c_str())
  }
}

Странно. Во всяком случае, я рефакторинг это что-то вроде этого:

extern "C" {
  string* GetString() {
    static string strFilename;
    return &strFilename;
  }

  void PrintToScreen() {
    printf("String: %s\n", GetString()->c_str())
  }
}

И теперь работает без проблем.
Тем не менее, мне кажется странным, что компилятор не жаловался.
Спасибо всем за вклад, проблема решена.

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

Позже я снова столкнулся с этой проблемой, поэтому она не была исправлена.
Настоящей проблемой был какой-то синглтон, который был инициализирован
тем временем и имел в конструкторе класса:

GetString() = "";

Итак, простая проблема, но очень трудно отследить ...

...