Разрешение зависимостей при включении заголовочных файлов в C++
От: FrozenHeart  
Дата: 24.02.14 12:03
Оценка:
Приветствую.

Имеется следующая ситуация (точнее, её упрощённый вариант, чтобы проблему можно было увидеть на минимальном примере):

Есть два заголовочных файла -- "exception.h" и "filesystem.h". Первый содержит определение базового класса исключений, используемых в проекте, а второй -- функции для различного взаимодействия с FS, а также определение класса исключений, наследуемого от того самого базового класса из "exception.h". "exception.h" же, в свою очередь, для реализации базового класса исключений нужно несколько функций из "filesystem.h".

Выглядит это всё примерно следующим образом:

// main.cpp

#include "exception.h"

int main() {}


// exception.h

#ifndef EXCEPTION_H
#define EXCEPTION_H

#include "filesystem.h"

class exception {};

#endif // !EXCEPTION_H


// filesystem.h

#ifndef FILESYSTEM_H
#define FILESYSTEM_H

#include "exception.h"

class filesystem_exception : public exception {};

inline
void foo() {}

#endif // !FILESYSTEM_H


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

error C2504: 'exception' : base class undefined


Как бы Вы исправили подобную ситуацию? Добавление необходимых в "exception.h" функций из заголовочного файла "filesystem.h" лишь успокоит компилятор, однако добавит дублирование кода.
avalon/1.0.434
Re: Разрешение зависимостей при включении заголовочных файлов в C++
От: real_sba http://cellwar.xyz/
Дата: 24.02.14 12:26
Оценка: +4
Здравствуйте, FrozenHeart, Вы писали:

FH>Выглядит это всё примерно следующим образом:

Пример уж слишком минимизированный.

FH> Добавление необходимых в "exception.h" функций из заголовочного файла "filesystem.h" лишь успокоит компилятор, однако добавит дублирование кода.

Скорее всего нет необходимости в зависимости базового класса исключений от filesystem.h. Не могу представить себе в каких случаях это действительно необходимо.
Re: Разрешение зависимостей при включении заголовочных файлов в C++
От: Erop Россия  
Дата: 24.02.14 14:28
Оценка: :)
Здравствуйте, FrozenHeart, Вы писали:


FH>Как бы Вы исправили подобную ситуацию? Добавление необходимых в "exception.h" функций из заголовочного файла "filesystem.h" лишь успокоит компилятор, однако добавит дублирование кода.


Я бы имел оба раза по файлу с декларациями и с реализациями. И Декларации fs зависели бы от деклараций исключений, а реализация обоих, зависелы от деклараций обоих...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Разрешение зависимостей при включении заголовочных файлов в C++
От: Кодт Россия  
Дата: 24.02.14 15:33
Оценка: +1
Здравствуйте, FrozenHeart, Вы писали:

FH>Есть два заголовочных файла -- "exception.h" и "filesystem.h". Первый содержит определение базового класса исключений, используемых в проекте, а второй -- функции для различного взаимодействия с FS, а также определение класса исключений, наследуемого от того самого базового класса из "exception.h".


FH> "exception.h" же, в свою очередь, для реализации базового класса исключений нужно несколько функций из "filesystem.h".


Либо здесь какая-то ошибка проектирования (зачем в exception.h файловая система?),
либо можно ограничиться forward declaration, как, например, <iostream> предобъявляет std::string, но не #include <string>
либо надо разнести объявления и определения — exception.h и exception-impl.h например, или filesystem.h и filesystem.cpp
Перекуём баги на фичи!
Re: Разрешение зависимостей при включении заголовочных файлов в C++
От: __kot2  
Дата: 24.02.14 16:34
Оценка:
Здравствуйте, FrozenHeart, Вы писали:
FH>
[ccode]
// exception.h

#ifndef EXCEPTION_H
#define EXCEPTION_H

#include "filesystem.h"

class exception {};

#endif // !EXCEPTION_H
[ccode]
FH>// exception.h
это зачем еще исключению знать детали реализации filesystem? не, ну может быть оно ей и правда нужно, но уж точно в реализации, а не в обьявлении. да и даже если в обьявлении вдруг приспичит, например, передавать исключению указатель на файл, то это решается так:

[ccode]
#ifndef EXCEPTION_H
#define EXCEPTION_H

//#include "filesystem.h" — нафиг
class File;

class exception {};

[ccode]
Re[2]: Разрешение зависимостей при включении заголовочных файлов в C++
От: FrozenHeart  
Дата: 24.02.14 19:47
Оценка:
_> это зачем еще исключению знать детали реализации filesystem?

Отвечал рядом:

Если вдаваться в конкретику, то в моём случае это код header-only библиотеки

avalon/1.0.434
Re[2]: Разрешение зависимостей при включении заголовочных файлов в C++
От: FrozenHeart  
Дата: 24.02.14 19:47
Оценка:
К> Либо здесь какая-то ошибка проектирования (зачем в exception.h файловая система?),

Например, для конструирования объекта класса debug::location, конструктор которого принимает в качестве аргумента строку, получаемую в результате "отделения" имени файла от полного пути до него (именно такая функция, помимо всего прочего, и есть в "filesystem.h").

К> либо можно ограничиться forward declaration, как, например, <iostream> предобъявляет std::string, но не #include <string>

К> либо надо разнести объявления и определения — exception.h и exception-impl.h например, или filesystem.h и filesystem.cpp

Если вдаваться в конкретику, то в моём случае это код header-only библиотеки.
avalon/1.0.434
Re[3]: Разрешение зависимостей при включении заголовочных файлов в C++
От: __kot2  
Дата: 24.02.14 20:38
Оценка:
Здравствуйте, FrozenHeart, Вы писали:
К>> Либо здесь какая-то ошибка проектирования (зачем в exception.h файловая система?),
FH>Например, для конструирования объекта класса debug::location, конструктор которого принимает в качестве аргумента строку, получаемую в результате "отделения" имени файла от полного пути до него (именно такая функция, помимо всего прочего, и есть в "filesystem.h").
вообще, все уши торчат из кривой библиотеки.
кстати, если библиотека задизайнена криво, то обычно там и багов полно. поэтому стоит ее проверить хорошенько, а лучше отказаться
если же говорить конструктивно — как все исправить, то можно облявление debug::location вынести в отдельный файл.
в любом случае обьявление exception не должно включать filesystem
Re[3]: Разрешение зависимостей при включении заголовочных файлов в C++
От: Кодт Россия  
Дата: 24.02.14 22:01
Оценка: +1
Здравствуйте, FrozenHeart, Вы писали:

FH>Например, для конструирования объекта класса debug::location, конструктор которого принимает в качестве аргумента строку, получаемую в результате "отделения" имени файла от полного пути до него (именно такая функция, помимо всего прочего, и есть в "filesystem.h").


Конкретно здесь это плохая затея.

Во-первых, расщепление строки на подстроки не должно кидать пользовательские исключения, особенно в тех случаях, когда происхождение строки (__FILE__) очевидно. Если компилятор сглючил и подсунул мусор, то кидать исключения поздно.
Так что можно было бы написать очень простую nothrow-версию, или версию, кидающую только std::bad_alloc из недр std::string.

Во-вторых, в солюшене запросто могут быть одноимённые файлы (в разных проектах), и даже в одном проекте (хедеры в разных каталогах).
Затирать пути — это лишать себя ценной информации.
Если структура каталогов компьютера, на котором собирается программа в продакшен, не должна быть засвечена, — то выход очень простой.
subst w: c:\path\to\your\sources
msbuild w:\solution\solution.sln

или аналогично с симлинками — хоть в виндах, хоть в никсах.

К>> либо можно ограничиться forward declaration, как, например, <iostream> предобъявляет std::string, но не #include <string>

К>> либо надо разнести объявления и определения — exception.h и exception-impl.h например, или filesystem.h и filesystem.cpp

FH>Если вдаваться в конкретику, то в моём случае это код header-only библиотеки.


<iostream> и <string> — тоже header-only, тоже содержат перекрёстные ссылки, — и там это разруливается предобъявлениями и шаблонами.

А разбиение хедера на файл с объявлениями и файл с инлайн-определениями позволит сделать потом мета-хедер, включающий все четыре части (объявления exception, объявления filesystem, определения того и другого) в нужном порядке.
Перекуём баги на фичи!
Re: Разрешение зависимостей при включении заголовочных файлов в C++
От: tstalker Украина  
Дата: 26.02.14 13:45
Оценка:
Здравствуйте, FrozenHeart, Вы писали:

FH>Как бы Вы исправили подобную ситуацию? Добавление необходимых в "exception.h" функций из заголовочного файла "filesystem.h" лишь успокоит компилятор, однако добавит дублирование кода.


Предлагаю следующий вариант:
// exception.h

#ifndef EXCEPTION_H
#define EXCEPTION_H

class exception
{
};

#ifndef FILESYSTEM_H
    #include "filesystem.h"
#endif // FILESYSTEM_H

#endif // EXCEPTION_H


// filesystem.h

#ifndef FILESYSTEM_H
#define FILESYSTEM_H

#ifndef EXCEPTION_H
    #include "exception.h"
#endif // EXCEPTION_H

class filesystem_exception: public exception
{
};

#endif // FILESYSTEM_H
Re[2]: Разрешение зависимостей при включении заголовочных файлов в C++
От: Ops Россия  
Дата: 27.02.14 15:19
Оценка:
Здравствуйте, Erop, Вы писали:

E>Я бы имел оба раза по файлу с декларациями и с реализациями. И Декларации fs зависели бы от деклараций исключений, а реализация обоих, зависелы от деклараций обоих...


А вообще такой спор был 2-3 года назад. Логика понятна, а проблемы решаются дефайнами или once
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.