Proyek C menghindari konflik penamaan

13

Saya berjuang untuk menemukan saran dunia nyata pragmatis tentang konvensi penamaan fungsi untuk proyek perpustakaan C berukuran sedang. Proyek perpustakaan saya dipisahkan menjadi beberapa modul dan submodul dengan tajuknya sendiri, dan secara longgar mengikuti gaya OO (semua fungsi menggunakan struct tertentu sebagai argumen pertama, tanpa global, dll). Itu meletakkan sesuatu seperti:

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

Secara umum fungsinya terlalu besar untuk (misalnya) menempel some_foo_actiondan another_foo_actiondalam satu foo.cfile implementasi, membuat sebagian besar fungsi statis, dan menyebutnya sehari.

Saya dapat menangani pengupasan simbol internal ("modul pribadi") saat membangun perpustakaan untuk menghindari konflik bagi pengguna saya dengan program klien mereka, tetapi pertanyaannya adalah bagaimana cara memberi nama simbol di perpustakaan saya? Sejauh ini saya sudah melakukan:

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

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

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

Tapi saya berakhir dengan nama simbol panjang gila (lebih lama dari contoh). Jika saya tidak mengawali nama dengan "ruang nama palsu", simbol internal modul menamai semua bentrok.

Catatan: Saya tidak peduli dengan camelcase / Pascal case dll, hanya namanya saja.

Dan Halliday
sumber

Jawaban:

10

Awalan (well, imbuhan) adalah satu-satunya pilihan. Beberapa pola yang akan Anda lihat adalah <library>_<name>(misalnya, OpenGL, runtime ObjC), <module/class>_<name>(misalnya, bagian dari Linux), <library>_<module/class>_<name>(misalnya, GTK +). Skema Anda sangat masuk akal.

Nama panjang tidak selalu buruk jika dapat diprediksi. Fakta bahwa Anda berakhir dengan nama-nama panjang yang gila - gilaan dan fungsi-fungsi yang terlalu besar untuk bertahan dengan fungsi-fungsi terkait dalam satu file sumber menimbulkan keprihatinan yang berbeda. Apakah Anda memiliki beberapa contoh nyata?

pengguna2313838
sumber
Saya melihat dari mana Anda berasal - Saya bertanya-tanya apakah saya terlalu pedantic tentang pemisahan menjadi file terpisah, tetapi banyak membantu dengan keterbacaan, pemeliharaan dan git mergeing. Sebagai contoh saya punya modul untuk menggambar UI dengan OpenGL, dan saya punya .cfile terpisah untuk setiap elemen yang saya butuhkan ( slider.c, indicator.cdll). Implementasi elemen ini memiliki fungsi menggambar utama mungkin beberapa ratus baris panjang, dan cukup banyak staticpembantu di dalamnya. Mereka juga memanggil beberapa fungsi geometri murni dari dalam modul UI. Apakah itu terdengar agak khas?
Dan Halliday
Contoh yang lebih baik dari nama-nama panjang mungkin modul audio saya - Saya memiliki hierarki seperti Audio Module > Engines > Channels > Filtersyang berarti sesuatu seperti MyLibAudioEngines<EngineName>Channel<ActionName>. Atau dalam submodule filter saya: MyLibAudioFilters<FilterName><Type><Action>mis. MyLibAudioFiltersBigSoundingCompressorFloat32Process
Dan Halliday
Tidak ada yang tampaknya tidak masuk akal. Beberapa ratus baris untuk suatu fungsi tampak agak panjang tetapi jika apa yang Anda gambar rumit, itu bisa sulit untuk dihindari. Apakah cabangnya banyak atau hanya banyak instruksi?
user2313838
Re: nama modul audio, Anda bisa menyingkat AudioFilters / AudioEngines karena saya pikir akan mudah untuk mengetahui apakah itu filter atau modul berdasarkan namanya. Kualifikasi Datatype seperti Float32 juga dapat disingkat (misalnya 'd', 'f') karena singkatan seperti itu biasa dalam pemrograman C.
user2313838
Terima kasih atas jawaban Anda - terkadang terasa mustahil untuk mendapatkan info bagus tentang arsitektur program C (terutama dibandingkan dengan bahasa tingkat yang lebih tinggi). Banyak buku C yang saya baca nyaris tidak mempertimbangkan gagasan memiliki banyak modul atau bahkan lebih dari satu file! Secara keseluruhan, saya tidak berpikir saya akan banyak berubah, kecuali mempertimbangkan apakah akan hidup dengan singkatan untuk beberapa nama yang lebih panjang.
Dan Halliday
5

Konvensi biasa untuk pustaka C adalah menggunakan nama pustaka sebagai awalan untuk nama yang dapat digunakan secara eksternal, misalnya

struct MyLibFoo;
void MyLibAFooAction(...);

Untuk nama perpustakaan-internal yang masih harus dapat diakses di beberapa unit perpustakaan, tidak ada konvensi yang ketat, tapi saya akan menggunakan awalan nama perpustakaan dan indikasi bahwa itu adalah fungsi internal. Sebagai contoh:

struct MyLibInternalFooBaz;
void MyLibInternalFooBazAction();

Saya setuju bahwa ini dapat menyebabkan nama yang cukup panjang dan sulit untuk diketik, tetapi sayangnya itu adalah harga yang harus kita bayar karena tidak memiliki mekanisme seperti ruang nama C ++. Untuk mengurangi panjang nama, dengan mengorbankan kejelasan, Anda dapat memilih untuk menggunakan singkatan dalam nama Anda, tetapi Anda harus mempertimbangkan kelebihan dan kekurangannya dengan cermat.

Bart van Ingen Schenau
sumber
3

Jika Anda ingin menghindari awalan panjang, Anda dapat menyingkat nama perpustakaan seperti yang dilakukan Apple di iOS dan OS X:

  • NSString adalah string dari akar NextStep dari OS
  • CALayer adalah lapisan Animasi Inti
mouviciel
sumber
2

Bagaimana dengan memiliki variabel struktur global yang diawali dengan pointer fungsi?

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

Memanfaatkan:

#include "lib.h"

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

Kata kunci statis membatasi ruang lingkup ke unit terjemahan sehingga tidak akan bertabrakan dengan orang lain.

Pengguna kemudian dapat mempersingkatnya dengan menggunakan pointer seperti YourApi *ya = &yourApi, lalu menggunakan ya->doFoo(...).

Ini juga menyediakan cara yang bagus untuk mengejek perpustakaan Anda untuk pengujian.

Calmarius
sumber
Pola yang keren, ini membutuhkan sedikit boilerplate, tetapi ini akan membuat autocomplete jauh lebih berguna.
Rick Love