C проект избегая конфликтов имен

13

Я изо всех сил пытаюсь найти практический практический совет по соглашениям об именах функций для проекта библиотеки C среднего размера. Мой библиотечный проект разделен на несколько модулей и подмодулей со своими собственными заголовками и слабо следует стилю ОО (все функции принимают определенную структуру в качестве первого аргумента, без глобальных переменных и т. Д.). Это заложено что-то вроде:

MyLib
  - Foo
    - foo.h
    - foo_internal.h
    - some_foo_action.c
    - another_foo_action.c
    - Baz
      - baz.h
      - some_baz_action.c
  - Bar
    - bar.h
    - bar_internal.h
    - some_bar_action.c

Как правило , функции слишком велики , чтобы (например) палка some_foo_actionи another_foo_actionв одном foo.cфайле реализации, делают большинство функций статичными, и называют его в день.

Я могу справиться с удалением моих внутренних («модульных») символов при сборке библиотеки, чтобы избежать конфликтов для моих пользователей с их клиентскими программами, но вопрос в том, как назвать символы в моей библиотеке? До сих пор я делал:

struct MyLibFoo;
void MyLibFooSomeAction(MyLibFoo *foo, ...);

struct MyLibBar;
void MyLibBarAnAction(MyLibBar *bar, ...);

// Submodule
struct MyLibFooBaz;
void MyLibFooBazAnotherAction(MyLibFooBaz *baz, ...);

Но я заканчиваю сумасшедшими длинными именами символов (намного длиннее, чем в примерах). Если я не добавлю имена к «поддельному пространству имен», имена внутренних символов модулей будут конфликтовать.

Примечание: меня не волнует случай с верблюдом / паскалем и т. Д., Только сами имена.

Дэн Холлидей
источник

Ответы:

10

Префикс (ну, приставка) действительно единственный вариант. Некоторые шаблоны, которые вы увидите <library>_<name>(например, OpenGL, ObjC runtime), <module/class>_<name>(например, части Linux), <library>_<module/class>_<name>(например, GTK +). Ваша схема вполне разумна.

Длинные имена не обязательно плохи, если они предсказуемы. Тот факт, что вы заканчиваете сумасшедшими длинными именами и функциями, которые слишком велики, чтобы придерживаться связанных функций в одном исходном файле, вызывает различные опасения. У вас есть более конкретные примеры?

user2313838
источник
Я вижу, откуда вы пришли - мне было интересно, слишком ли я педантичен к разделению на отдельные файлы, но это очень помогает с удобочитаемостью, обслуживанием и git mergeзагрузкой. В качестве примера у меня есть модуль для рисования пользовательского интерфейса с OpenGL, и у меня есть отдельные .cфайлы для каждого элемента, который мне нужен ( slider.cи indicator.cт. Д.). Эти реализации элементов имеют основную функцию рисования длиной в несколько сотен строк и достаточное количество staticпомощников внутри. Они также вызывают несколько чисто геометрических функций из модуля пользовательского интерфейса. Это звучит довольно типично?
Дэн Хэллидей
Лучшим примером длинных имен может быть мой аудио модуль - у меня есть иерархия, Audio Module > Engines > Channels > Filtersкоторая означает что-то вроде MyLibAudioEngines<EngineName>Channel<ActionName>. Или в моем подмодуле фильтров: MyLibAudioFilters<FilterName><Type><Action>например. MyLibAudioFiltersBigSoundingCompressorFloat32Process
Дэн Хэллидей
Ничто из этого не кажется необоснованным. Несколько сотен строк для функции кажутся немного длинными, но если то, что вы рисуете, является сложным, этого может быть трудно избежать. Это ветка тяжелая или просто много инструкций?
user2313838
Что касается имен аудио модулей, вы можете сокращать AudioFilters / AudioEngines, так как я думаю, что было бы легко определить, является ли это фильтр или модуль, основанный на имени. Спецификаторы типов данных, такие как Float32, также могут быть сокращены (например, 'd', 'f'), поскольку такие сокращения распространены в программировании на Си.
user2313838
Спасибо за ваши ответы - иногда кажется почти невозможным получить хорошую информацию об архитектуре программы на Си (особенно по сравнению с языками более высокого уровня). Многие книги по Си, которые я читал, едва ли учитывают идею наличия нескольких модулей или даже нескольких файлов! В целом, я не думаю, что я сильно изменится, кроме как подумать, стоит ли жить с аббревиатурами для некоторых более длинных имен.
Дэн Хэллидей
5

Обычное соглашение для библиотек C - использовать имя библиотеки в качестве префикса для внешне используемых имен, например

struct MyLibFoo;
void MyLibAFooAction(...);

Для внутренних библиотечных имен, которые все еще должны быть доступны в нескольких единицах библиотеки, не существует строгого соглашения, но я бы использовал префикс имени библиотеки и указание на то, что это внутренняя функция. Например:

struct MyLibInternalFooBaz;
void MyLibInternalFooBazAction();

Я согласен, что это может привести к тому, что имена будут довольно длинными и сложными для ввода, но, к сожалению, это цена, которую мы должны заплатить за отсутствие такого механизма, как пространства имен C ++. Чтобы уменьшить длину имен, ценой некоторой ясности, вы можете использовать сокращения в своих именах, но вы должны тщательно взвесить преимущества и недостатки.

Барт ван Инген Шенау
источник
3

Если вы хотите избежать длинных префиксов, вы можете сокращать имена библиотек, как то, что Apple делает в iOS и OS X:

  • NSString - это строка из корней NextStep ОС
  • CALayer - это слой Core Animation
mouviciel
источник
2

Как насчет наличия глобальной структурной переменной, предварительно заполненной указателями на функции?

lib.h

#pragma once

typedef struct
{
    void (*doFoo)(int x);
    const char *(*doBar)(void *p);
} YourApi;

extern const YourApi yourApi;

lib.c:

#include "lib.h"

#include <stdio.h>

static void doFoo(int x)
{
    printf("Doing foo %d\n", x);
}

static const char *doBar(void *p)
{
    printf("Doing bar: %p\n", p);
    return "Hello";
}

const YourApi yourApi = {
    doFoo,
    doBar};

Упряжь:

#include "lib.h"

int main()
{
    yourApi.doFoo(42);
    yourApi.doBar("asd");
}

Ключевое слово static ограничивает область действия модулем перевода, чтобы оно не сталкивалось с другими.

Затем пользователь может сократить его, используя указатель, например YourApi *ya = &yourApi, затем используя ya->doFoo(...).

Он также предоставляет хороший способ для проверки вашей библиотеки для тестирования.

Calmarius
источник
Прикольная схема, она требует небольшого количества шаблонов, но это сделает автозаполнение намного более полезным.
Рик Лав