#c #namespaces #porting
#c #пространства имен #перенос
Вопрос:
Я переношу некоторый открытый исходный код, чтобы его можно было использовать на другом компиляторе c . Одна из трудностей и тем, которые, похоже, продолжают возникать, — это различия в реализации предоставляемой компилятором стандартной библиотеки.
Например, один из исходных файлов, которые я компилирую, включает <sys/types.h>
. Однако это выдает мне следующие ошибки:
Error E2316 g:BorlandBCC593Includesys/types.h 45: 'time_t' is not a member of 'std'
Error E2272 g:BorlandBCC593Includesys/types.h 45: Identifier expected
Посмотрев на первопричину этого, я обнаружил, что одним из основных заголовков include проекта является включение <sys/types.h>
в этот шаблон:
project_source1.cpp:
#include "../TargetPlatform.h"
#include "project_config.h"
#include <windows.h>
namespace project_namespace {
#include "project_component/all.h"
// more project includes down here
// ...
}
project_component/all.h:
#ifndef INCLUDE_GUARDS
#define INCLUDE_GUARDS
#include <sys/types.h>
#include "project_header1.h"
#include "project_header2.h"
// and other headers etc..
// followed with class definitions and what not.
#endif
Это все хорошо, за исключением одной проблемы, <sys/types.h>
для компилятора, на который я портирую, реализовано что-то вроде этого:
<sys/types.h>
урезано до сути:
namespace std {
typedef long time_t;
typedef short dev_t;
typedef short ino_t;
typedef short mode_t;
typedef short nlink_t;
typedef int uid_t;
typedef int gid_t;
typedef long off_t;
} // std
using std::time_t;
using std::dev_t;
using std::ino_t;
using std::mode_t;
using std::nlink_t;
using std::uid_t;
using std::gid_t;
using std::off_t;
И это причина ошибки компиляции, которую я вижу. Поскольку проект включает в себя <sys/types.h>
внутри своего собственного пространства имен, такие вещи, как time_t, off_t, dev_t и т.д., Попадают в область видимости, project_namespace::std::
Что явно не то, что задумано.
Каков наилучший способ справиться с этим? Имейте в виду, что могут быть другие заголовки стандартной библиотеки, определенные аналогичным образом, и не только sys/types.h
. Существуют ли какие-либо идиомы C , которые имеют отношение к этой проблеме (или, возможно, даже расходятся с ней из-за способа ее реализации)? Если да, то как это можно согласовать?
Спасибо
Ответ №1:
Это плохая идея. Не делайте ничего таким образом. Поместите объявления пространства имен внутри каждого файла заголовка. Никогда не размещайте #include
директиву внутри пространства имен.
«Никогда» — сильное слово, и есть очень редкие случаи, в которых вы, возможно, захотите это сделать. Если у файла, который вы #include
редактируете, также есть #include
директивы, вы почти наверняка не захотите этого делать, даже больше, чем если бы их не было.
Если вам нужно иметь возможность легко переименовывать свое пространство имен или использовать один и тот же заголовок для объявления одних и тех же символов в нескольких разных пространствах имен в зависимости от контекста, используйте препроцессор для изменения имени пространства имен вместо этого. Это крайне некрасиво делать, но намного лучше, чем то, что вы делаете в настоящее время.
Единственное, что вы можете сделать в качестве быстрого и грязного решения этой проблемы, — это #include <sys/types.h>
и любой другой системный заголовок, который включен перед объявлением пространства имен. Это приведет к срабатыванию защиты двойного включения в файлах системных заголовков и позволит избежать объявления содержимого внутри пространства имен.
Комментарии:
1. Спасибо за информацию. Нет, это не то, что я делаю. Скорее это то, что проект уже делает. Я просто случайно наткнулся на это из-за возникающих из-за этого проблем. Хотя я не уверен, почему оригинальный автор сделал это таким образом.
2. @Виктор Т.: Я дал вам предложение относительно того, как вы могли бы исправить ситуацию. Но на самом деле, вы должны просто исправить это, чтобы оно больше этого не делало. Тот, кто решил сделать это таким образом, в первую очередь, принял очень плохое решение.
Ответ №2:
Я бы сказал, что вам нужно изменить код-нарушитель, чтобы стандартные типы больше не определялись внутри project_namespace
.
Мне кажется, у вас есть две основные проблемы: [1] у вас есть универсальный заголовок в project_all.h
и [2] он #include
общий внутри namespace project_namespace
. Сначала я обращусь к последнему. Если в вашем проекте есть вещи, которые он хочет поместить в свое собственное пространство имен, это нормально, но это должно быть сделано следующим образом:
// Foo.h
// includes go here
namespace project_namespace {
class Foo { ... }
}
// Foo.cpp
// includes go here
namespace project_namespace {
// Foo implementation goes here
}
… другими словами, в заголовках ваших классов и файлах реализации должно быть указано, к какому пространству имен принадлежат классы, так сказать, к «самостоятельному пространству имен». Все ваши #include
s находятся за пределами a namespace
, потому что файл заголовка для каждого класса объявляет пространства имен, к которым принадлежит класс. Это соглашение, используемое стандартной библиотекой.
Теперь, если вы все еще хотите использовать project_all.h
, вы можете это сделать. Без пространства имен — потому что каждый заголовочный файл будет размещать свои объявления в том пространстве имен, в котором он хочет (или нет).
Но было бы лучше отказаться от project_all.h
; вместо этого #include
файлы заголовков по отдельности и сделать зависимости явными. И если ответ «слишком много заголовочных файлов», это, вероятно, признак высокой степени связи между вашими компонентами.
Я понимаю, что это, вероятно, не тот ответ, который вы хотели, извините…
Комментарии:
1. Это хороший ответ, и, тем не менее, я нашел его полезным. Я посмотрю, смогу ли я изменить его, чтобы оно лучше соответствовало этому шаблону. Однако я хотел бы свести необходимые изменения к минимуму. Я предполагаю, что у первоначального автора был all.h для предварительно скомпилированных заголовочных целей.
2. Что касается предварительно скомпилированных заголовков:
#include
они документируют ваши зависимости; Я бы сказал, что это важная функция, важность которой возрастает со временем, когда разработчики входят в команду и выходят из нее. Я понимаю проблему с предварительно скомпилированным заголовком / временем сборки (и я чувствую вашу боль), но есть лучшие способы решить эту проблему. Допустим, вы подключаетесь к датчикам температуры и влажности. Каждый из этих интерфейсов ввода-вывода может (и, вероятно, должен) быть упакован в свою собственную DLL; тогда вам не придется каждый раз (заново) собирать этот код вместе с вашим «основным» приложением.3. Я также понимаю ваше нежелание вносить много изменений сразу (и это нормальная реакция). Мой совет был бы сделать ваши изменения небольшими и постепенными. А еще лучше, создавайте модульные тесты вокруг них по ходу работы, чтобы у вас был набор регрессионных тестов для проверки того, что вы ничего не нарушили.
4. Странный вопрос — проводите ли вы различие между «существует много заголовков» и «существует слишком много заголовков»? Я не использую «…all.hpp», но я склонен отделять несколько тесно или циклически зависимых классов / шаблонных классов, например, quaternion / vector / matrix, путем прямого объявления их в одном заголовке, таком как «math.hpp», и (иногда) включая каждый отдельный заголовок из сгруппированного заголовка и наоборот. Прав ли я, предполагая, что искусственное разделение / преднамеренная взаимозависимость также подразумевает проблемы искусственного соединения? Или есть «лучший» подход? (Извините за 6-летний некропост.)
5. Мои основные опасения заключались бы в том, что (1) добавленный уровень косвенности увеличивает сложность понимания для новых разработчиков — к которым я бы присоединился после нескольких месяцев работы над другим проектом — и (2) что я бы нарушил принцип разделения интерфейса .