Включение источников и заголовков в оболочку верхнего уровня C

Я наткнулся на следующий код:

//
// Top-level file that includes all of the C/C++ files required
//
// The C code may be compiled by compiling this top file only,
// or by compiling individual files then linking them together.

#ifdef __cplusplus
extern "C" {
#endif

#include <stdlib.h>
#include "my_header.h"#include "my_source1.cc"#include "my_source2.cc"
#ifdef __cplusplus
}
#endif

Это определенно необычный но считается ли это плохой практикой и если да, то почему?

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

1

Решение

Прежде всего: extern "C" { #include "my_cpp_file.cc" } просто не складывается … во всяком случае, я попытаюсь ответить на ваш вопрос на практическом примере.
Обратите внимание, что иногда вы делать увидеть #include "some_file.c" в исходном файле. Часто это происходит потому, что код в другом файле находится в стадии разработки, или нет уверенности в том, что функция, которая разрабатывается в этом файле, сделает релиз.

Другая причина довольно проста: улучшить читабельность: не нужно слишком много прокручивать) или даже: отражать многопоточность. Кому-то помогает хранение кода ребенка в отдельном файле, особенно при изучении потоков.

Конечно, основное преимущество включения модулей перевода в один основной модуль перевода (который, на мой взгляд, злоупотребляет препроцессором, но это не главное), прост: меньше операций ввода-вывода при компиляции и, следовательно, более быстрая компиляция. Здесь все объяснено.

Это одна сторона истории, хотя. Эта техника не идеально. Вот пара соображений. И просто, чтобы сбалансировать «магия единства строит» статья, вот «зло строит единство» статья.

В любом случае, вот небольшой список моих возражений и несколько примеров:

  • static глобальные переменные (честно говоря, мы все их использовали)
  • extern а также static функции одинаковы: оба могут быть вызваны везде
  • Отладка потребует от вас сборки все, если (как «Про» статья предлагает) иметь Unity-Build и modular-build для одного и того же проекта. ИМО немного бред
  • Не подходит, если вы хотите извлечь библиотеку из вашего проекта, которую вы хотите использовать позже (подумайте об общих библиотеках или библиотеках DLL)

Просто сравните эти две ситуации:

//foo.h
struct foo
{
char *value;
int checksum;
struct foo *next;
};

extern struct foo * get_foo(const char *val);

extern void free_foo( struct foo **foo);

//foo.c
#include <foo.h>
static int get_checksum( const char *val);
struct foo * get_foo( const char *val)
{
//call get_checksum
struct foo *retVal = malloc(sizeof *retVal);
retVal->value = calloc(strlen(val) + 1, 1);
retVal->cecksum = get_checksum(val);
retVal->next = NULL;
return retVal;
}
void free_foo ( struct foo **foo)
{
free(*foo->value);
if (*foo->next != NULL)
free_foo(&(*foo->next));
free(*foo);
*foo = NULL;
}

Если бы я включил этот файл C в другой исходный файл, get_checksum Функция также будет вызываться в этом файле. Вот это не дело.
Конфликты имен также будут встречаться гораздо чаще.

Представьте также, если вы написали некоторый код, чтобы легко выполнять определенные быстрые запросы MySQL. Я бы написал свой собственный заголовок и исходные файлы и скомпилировал их так:

gccc -Wall -std=c99 mysql_file.c `mysql_config --cflags --libs` -o mysql.o

И просто используйте это mysql.o скомпилированный файл в других проектах, связав его просто так:

//another_file.c
include <mysql_file.h>

int main ( void )
{
my_own_mysql_function();
return 0;
}

Который я могу затем скомпилировать так:

gcc another_file.c mysql.o -o my_bin

Это экономит время разработки, время компиляции и упрощает управление проектами (при условии, что вы разбираетесь в файле make).

Еще одним преимуществом этих файлов .o является совместная работа над проектами. Предположим, я бы объявил о новой функции для нашего mysql.o файл. Все проекты, в которых мой код является зависимостью, могут безопасно продолжать использовать последнюю стабильно скомпилированную mysql.o файл, пока я работаю над своим фрагментом кода.

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

1

Другие решения

Проблема в том, что каждый из ваших *.cc файлы будут компилироваться каждый раз, когда включается заголовок.

Например, если у вас есть:

// foo.cc:

// also includes implementations of all the functions
// due to my_source1.cc being included
#include "main_header.h"

А также:

// bar.cc:

// implementations included (again!)
// ... you get far more object code at best, and a linker error at worst
#include "main_header.h"

Несвязанный, но все еще актуальный: иногда у компиляторов возникают проблемы, когда ваши заголовки включают заголовки C stdlib в код C ++.

Редактировать: Как уже упоминалось выше, существует также проблема наличия extern "C" вокруг ваших источников C ++.

0

Это определенно необычно, но считается ли это плохой практикой, и если да, то почему?

Вы, вероятно, смотрите на «Юнити Билд». Сборки Unity — хороший подход, если они настроены правильно. Может быть проблематично сконфигурировать библиотеку для первоначального построения таким образом, потому что могут быть конфликты из-за расширенной видимости — включая реализации, которые автор задумал сделать закрытыми для перевода.

Тем не менее, определения (в *.cc) должен быть снаружи, если extern "C" блок.

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

Это уменьшает зависимость / сложность, потому что количество переводов уменьшается.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector