#c #include #conditional-compilation
Вопрос:
Я пишу некоторую библиотеку и хочу, чтобы некоторые «необязательные» методы класса (или просто функции), объявленные или нет, зависели от включения других библиотек.
Скажем, у меня есть класс SomeClass
с методом int foo(std::string)
. Иногда очень полезно также иметь аналогичные методы, которые используют классы другой библиотеки , на которой построен проект, например, sf::String
или wxString
, соответственно, для SFML или wxWidgets.
В этом случае включение SFML/System.hpp
или даже хуже, wx/app.hpp
или подобное абсолютно НЕ вариант, потому что я хочу иметь только методы для библиотек, которые уже включены. Итак, мой первый пример должен (как я полагаю) работать нормально, но это не так:
main.cpp:
#include <SFML/System.hpp> // FIRST, I include SFML base lib in the very first line.
#include <SFML/System/String.hpp>// to be 100% sure, I include SFML string class,
#include "a.h" // and ONLY AFTER that I include my own lib
// so inside the "a.h" file, the sf::String class *must* be already declared
main()
{ SomeClass x;
x.foo("ABC");// error here: "undefined reference to `SomeClass::foo(sf::String)"
}
a.h:
#ifndef A_H_INCLUDED
#define A_H_INCLUDED
class SomeClass
{ public:
#ifdef SFML_STRING_HPP
int foo(sf::String str);// this method is declared, as expected
#endif
};
#endif
a.cpp:
#include "a.h"
#ifdef SFML_STRING_HPP
int SomeClass::foo(sf::String str)
{ return 1;
}
#endif
Первый вопрос: ПОЧЕМУ? a.cpp
включает a.h
в себя в самом начале, а внутри a.h
sf::String
объявлено, так почему же внутри a.cpp
после #include "a.h"
того, как оно не объявлено на самом деле?
Я попытался добавить #error OK
#endif
директиву прямо перед вводом в a.cpp
файл, и эта ошибка не сработала.
Я что-то пропустил о #include
и .cpp
/ .h
файлах?..
Второй вопрос: как это исправить или обойти?
(И да, я каждый раз делаю чистую перестройку, чтобы избежать возможных ошибок компилятора в отношении частично измененных источников, g это нравится).
P. S: Такого же рода объявления «зависимых» методов отлично работают с некоторыми классами шаблонов — я полагаю, это потому, что реализация находится в .h
файле, где все в порядке с условной компиляцией.
Комментарии:
1. Небольшой совет: вы можете сделать
#include
операторы также необязательными, поместив их в#ifdef
области препроцессора. Таким образом, вы можете включить заголовок строки в то же условие, которое вы объявляете, и определить функцию, которая его использует.2. Определения классов, которые меняются в зависимости от контекста, рано или поздно вас укусят. Не делай этого.
3. Пит, почему он будет кусаться? Я объявляю и определяю разные методы, поэтому в проекте на основе SFML я буду использовать
fooSM(sf::String)
, в проекте на основе wxWidgets я буду использоватьfooWX(wxString)
метод. Это позволяет мне сохранять основной код библиотеки одинаковым для многих различных проектов, а также избегать избыточного преобразования типов (используйте собственные типы в зависимости от базовых библиотек проекта).
Ответ №1:
a.c включает в себя a.h, который не включает <SFML/System/String.hpp>, поэтому SFML_STRING_HPP не определен. Обычно то, что нужно включить, задается с помощью параметров компилятора-D. Например-DUSE_SFML_STRING
main.cpp
#include <SFML/System.hpp> // FIRST, I include SFML base lib in the very first line.
#include "a.h" // and ONLY AFTER that I include my own lib
// so inside the "a.h" file, the sf::String class *must* be already declared
main()
{ SomeClass x;
x.foo("ABC");// error here: "undefined reference to `SomeClass::foo(sf::String)"
}
a.h
#ifndef A_H_INCLUDED
#define A_H_INCLUDED
#ifdef USE_SFML_STRING
#include <SFML/System/String.hpp>
#endif
class SomeClass
{ public:
#ifdef SFML_STRING_HPP
int foo(sf::String str);// this method is declared, as expected
#endif
};
#endif
Комментарии:
1. Сначала я думал, что все скомпилировано, начиная с main.cpp и в том числе то, что необходимо, но это не так. Добавление определения для всего проекта полностью решает мою проблему. Спасибо!