Projeto C evitando conflitos de nomenclatura

13

Estou lutando para encontrar conselhos pragmáticos do mundo real sobre convenções de nomenclatura de funções para um projeto de biblioteca C de tamanho médio. Meu projeto de biblioteca é separado em alguns módulos e submódulos com seus próprios cabeçalhos e segue um estilo OO (todas as funções assumem uma certa estrutura como primeiro argumento, sem globais etc). É colocado nosso algo como:

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

Geralmente as funções são muito grandes demais para (por exemplo) vara some_foo_actione another_foo_actionem um foo.carquivo de implementação, fazer mais funções estático, e chamá-lo um dia.

Posso lidar com a remoção de meus símbolos internos ("módulo privado") ao criar a biblioteca para evitar conflitos para meus usuários com seus programas clientes, mas a questão é como nomear símbolos na minha biblioteca? Até agora eu venho fazendo:

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

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

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

Mas estou terminando com nomes de símbolos longos e loucos (muito mais do que os exemplos). Se eu não prefixo os nomes com um "espaço para nome falso", os símbolos internos dos módulos são todos conflitantes.

Nota: Eu não ligo para o caso camelcase / Pascal, etc., apenas os nomes em si.

Dan Halliday
fonte

Respostas:

10

Prefixar (bem, afixar) é realmente a única opção. Alguns padrões que você verá são <library>_<name>(por exemplo, OpenGL, ObjC runtime), <module/class>_<name>(por exemplo, partes do Linux), <library>_<module/class>_<name>(por exemplo, GTK +). Seu esquema é perfeitamente razoável.

Nomes longos não são necessariamente ruins se são previsíveis. O fato de você estar terminando com nomes longos malucos e funções grandes demais para ficar com as funções relacionadas em um único arquivo de origem gera preocupações diferentes. Você tem alguns exemplos mais concretos?

user2313838
fonte
Eu vejo de onde você vem - eu me perguntei se eu estava sendo muito pedante em dividir em arquivos separados, mas isso ajuda muito na legibilidade, manutenção e git mergeing. Como exemplo, tenho um módulo para desenhar a interface do usuário com o OpenGL e tenho .carquivos separados para cada elemento que preciso ( slider.c, indicator.cetc). Essas implementações de elementos têm uma função principal de desenho, talvez com algumas centenas de linhas e um bom número de staticauxiliares. Eles também chamam algumas funções de geometria pura de dentro do módulo de interface do usuário. Isso soa bastante típico?
Dan Halliday
Um exemplo melhor dos nomes longos pode ser meu módulo de áudio - eu tenho uma hierarquia, o Audio Module > Engines > Channels > Filtersque significa algo como MyLibAudioEngines<EngineName>Channel<ActionName>. Ou no meu sub-módulo de filtros: MyLibAudioFilters<FilterName><Type><Action>por exemplo. MyLibAudioFiltersBigSoundingCompressorFloat32Process
Dan Halliday
Nada disso parece irracional. Algumas centenas de linhas para uma função parecem um pouco longas, mas se o que você está desenhando é complicado, pode ser difícil evitar isso. É ramo pesado ou apenas um monte de instruções?
user2313838
Re: os nomes dos módulos de áudio, você pode abreviar AudioFilters / AudioEngines, pois acho que seria fácil saber se é um filtro ou módulo com base no nome. Qualificadores de tipo de dados como o Float32 também podem ser abreviados (por exemplo, 'd', 'f'), pois essas abreviações são comuns na programação C.
user2313838
Obrigado por suas respostas - às vezes parece quase impossível obter boas informações sobre a arquitetura do programa C (especialmente em comparação com os idiomas de nível superior). Muitos livros em C que eu li mal consideram a idéia de ter vários módulos ou até mais de um arquivo! No geral, acho que não vou mudar muito, exceto considerar se devo usar abreviações para alguns dos nomes mais longos.
Dan Halliday
5

A convenção usual para bibliotecas C é usar o nome da biblioteca como prefixo para nomes utilizáveis ​​externamente, por exemplo

struct MyLibFoo;
void MyLibAFooAction(...);

Para nomes internos da biblioteca que ainda devem estar acessíveis em várias unidades da biblioteca, não existe uma convenção estrita, mas eu usaria um prefixo do nome da biblioteca e uma indicação de que é uma função interna. Por exemplo:

struct MyLibInternalFooBaz;
void MyLibInternalFooBazAction();

Concordo que isso pode levar a nomes bastante longos e difíceis de digitar, mas infelizmente esse é o preço que temos que pagar por não ter um mecanismo como os espaços para nome C ++. Para reduzir o tamanho dos nomes, ao custo de alguma clareza, você pode optar por usar abreviações em seus nomes, mas é necessário avaliar cuidadosamente as vantagens e desvantagens.

Bart van Ingen Schenau
fonte
3

Se você deseja evitar prefixos longos, é possível abreviar nomes de bibliotecas como o que a Apple faz no iOS e no OS X:

  • NSString é uma sequência das raízes NextStep do SO
  • O CALayer é uma camada de animação principal
mouviciel
fonte
2

Que tal ter uma variável de estrutura global pré-preenchida com ponteiros de função?

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};

Arnês:

#include "lib.h"

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

A palavra-chave estática limita o escopo à unidade de tradução para não colidir com outras pessoas.

O usuário pode encurtá-lo usando um ponteiro como YourApi *ya = &yourApi, em seguida, usando ya->doFoo(...).

Ele também fornece uma boa maneira de zombar da sua biblioteca para teste.

Calmarius
fonte
Padrão legal, requer um pouco de clichê, mas isso tornará o preenchimento automático muito mais útil.
Rick Love