Проблемы с компиляцией глобального пространства имен С++

Я использую определенный набор инструментов (тот, который поставляется с BlackBerry Playbook NDK). Я думаю, что это основано на GCC 4.4.2. Когда я пытаюсь скомпилировать определенные библиотеки с помощью этой цепочки инструментов, я сталкиваюсь со странными проблемами с тем, что я считаю глобальным пространством имен С++. Например, я получу такие ошибки, как:

'::sprintf' has not been declared

Я никогда не получаю эти ошибки при компоновке со стандартной библиотекой GNU C++. Однако Playbook NDK по умолчанию использует библиотеки Dinkumware C++, и мне всегда приходится просматривать каждую ошибку и обычно добавлять эквивалент C (в данном случае stdio.h). По не зависящим от меня причинам я не могу установить ссылку на стандартную библиотеку GNU c++, в которой нет ни одной из этих проблем.

Если я пытаюсь скомпилировать более крупный проект, я могу получить сотни таких ошибок. Есть ли обходной путь, не связанный с редактированием источника? В приведенном выше примере файл обычно включает «cstdio» в качестве заголовка, но это объявляет sprintf в пространстве имен «std». Таким образом, у меня есть только один вариант: включить заголовок C или добавить пространство имен std.


person Prismatic    schedule 10.06.2012    source источник
comment
Вы должны добавить <cstdio>, а не stdio.h   -  person K-ballo    schedule 10.06.2012
comment
Извините, я отредактировал свой пост, чтобы отразить то, что происходит. Исходный файл обычно включает cstdio. Но это вызовет ошибку, если я не добавлю вызов функции в пространстве имен std.   -  person Prismatic    schedule 10.06.2012
comment
Его проблема в том, что <cstdio> объявляет все в пространстве имен std. Он использует <stdio.h>, чтобы обойти это, и хотел знать, есть ли другой способ. Я не думаю, что есть, кроме префикса std или использования using namespace std;.   -  person Shoaib    schedule 10.06.2012
comment
Использование GNU STL не решает эту проблему.   -  person Ken Bloom    schedule 10.06.2012
comment
Это для меня. Чтобы было ясно, я пытаюсь портировать проекты, используя эту конкретную цепочку инструментов. Если я попытаюсь скомпилировать те же проекты на своем настольном компьютере, у меня не будет никаких проблем, все будет работать. Но тот же проект не будет компилироваться с набором инструментов. Кроме того, если я заставлю цепочку инструментов использовать драйвер g++ вместо стандартного драйвера qcc, все будет нормально компилироваться. Разница между ними заключается в ссылках драйвера qcc на библиотеки Dinkumware и ссылках g++ на GNU stl. Других различий между этими двумя методами я не знаю.   -  person Prismatic    schedule 10.06.2012


Ответы (3)


На самом деле стандарт C++ предписывает, чтобы все символы, объявленные в стандартной библиотеке (включая библиотеку C в заголовках <c....>), были доступны в пространстве имен std::. Это не запрещает им также появляться в глобальном пространстве имен, и некоторые реализации стандартной библиотеки C++, такие как libstdc++, решили допустить их туда по соображениям обратной совместимости.

У вас есть несколько возможностей:

  • вы можете продолжать включать заголовки C напрямую (например, <stdio.h>), однако обычно это не рекомендуется, поскольку они могут не поддерживать C++ и могут создавать конфликты (1)
  • вы можете префиксировать каждый вызов, хотя это может быть непрактично
  • вы можете выбирать символы, которые используете: using std::printf; либо вверху файла, либо внутри каждой функции
  • можно прямо поставить директиву using namespace std;. Однако вы будете тянуть много вещей ...

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

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

// cstdio.hpp

#include <cstdio>

#if defined(_DIRKUMWARE_) // note: you'll have to research the symbol to check
namespace yournamespace {
    using std::printf;
    using std::vptrinf;
} // namespace yournamespace
#endif // defined(_DIRKUMWARE_)

А затем пусть скрипт заменит все вхождения <stdio.h> и <cstdio> в базе кода на <cstdio.hpp> (очевидно, кроме этого файла). Таким образом, вы можете настроить этот файл для каждой реализации стандартной библиотеки, вместо того, чтобы повторять обходные пути в каждом файле, который включает эти функции.

Примечание (1): стандарт C не определяет, должна ли функциональность предоставляться функцией или макросом. Как правило, min и max могут быть макросами. Стандарт C++ предписывает функции, поэтому использование заголовков C++ обычно безопаснее.

person Matthieu M.    schedule 10.06.2012

По сути, вы застряли в углу.

Согласно стандарту С++:

Включение cstdio импортирует имена символов в пространство имен std и возможно в глобальное пространство имен.

и

Включение stdio.h импортирует имена символов в глобальное пространство имен и возможно в стандартное пространство имен.

Таким образом, если в конкретной реализации, включая cstdio, не импортируются символы в глобальном пространстве имен, тогда единственный вариант, который у вас есть, — это использовать полное имя для пространства имен std.
Это связано с тем, что поведение четко определено стандартом C++. просто не так широко известен.

person Alok Save    schedule 10.06.2012

Если есть несколько общих заголовочных файлов, из которых включаются исходные тексты, вы можете просто изменить их с помощью необходимых включений.

GCC имеет параметр командной строки под названием -include, который позволяет принудительно включить исходный файл в файл до того, как остальная его часть будет скомпилирована. Вы можете изменить свой Makefile или эквивалент скрипта сборки проекта, чтобы сделать включения за вас.

// c_includes.h
#include <stdio.h>
#include <stdlib.h>
//...

g++ -include c_includes.h ...
person jxh    schedule 10.06.2012