Hacer que la función sea visible solo dentro de una biblioteca, no en API

Nov 29 2020

Estoy escribiendo una biblioteca C99 que se distribuye entre varios archivos, por ejemplo

// core.h
void my_private_fn();

void API_my_public_fn();
// core.c
#include "core.h"

void my_private_fn() {
    do_something();
}

void API_my_public_fn() {
    do_something_else();
}
// module_a.h
#include "core.h"

void API_useful_thing();
// module_a.c
#include "module_a.h"

void API_useful_thing() {
    my_private_fn();
}

Quiero solamente las API_funciones prefijadas sean visibles por un programa de uso de la biblioteca, pero también necesito para exponer my_private_fnen core.hel fin de ser utilizado por module_a.c. ¿Hay alguna forma en C de hacer my_private_fnsolo visible dentro de la biblioteca?

Respuestas

3 SergeBallesta Nov 29 2020 at 01:39

Si la función solo tuviera que estar visible en la unidad de compilación donde está definida, entonces podría declararla static. Debido a que el lenguaje C ofrece pocos ámbitos posibles: un símbolo solo puede tener 3 ámbitos:

  • local a un bloque (el bloque puede ser una función o un bloque dentro de una función)
  • alcance estático (declaración estática fuera de una función): el símbolo solo es visible en la unidad de compilación donde se declara
  • alcance global (declaración no estática fuera de una función): el símbolo es visible en todo el programa.

Como máximo, puede ocultar la declaración en un archivo de inclusión privado que no declara en la API oficial documentada. De esa manera, los usuarios obedientes no deberían usarlo. Pero no puede evitar que los usuarios pongan la declaración en su propio código y utilicen la función.

2 JohnKugelman Nov 29 2020 at 01:35

Ponerlos en un archivo de cabecera interna que sólo se utiliza dentro de la biblioteca y no se distribuye a los usuarios finales, por ejemplo, core_internal.h.

user3758232 Dec 31 2020 at 00:19

Encontré una manera más ordenada de diseñar mi código basándose en la respuesta de Serge que seleccioné, cuyo mérito es mayor.

La clave es poner las funciones "privadas" en encabezados que solo se incluyen en archivos C, no en archivos de encabezado. De esta manera, los símbolos "privados" están disponibles internamente pero no para un llamante externo. En un ejemplo completo:

core.h:

void my_public_fn();

core_priv.h:

void my_private_fn();

core.c:

#include <stdio.h>

#include "core.h"
#include "core_priv.h"

void my_private_fn() {
    printf("Private function called.\n");
}

void my_public_fn() {
    printf("Public function called.\n");
}

module_a.h:

#include "core.h"

void module_a_fn();

module_a.c:

#include "core_priv.h"
#include "module_a.h"

void module_a_fn() {
    my_private_fn();
    my_public_fn();
}

Y si queremos, podemos agrupar posiblemente varios módulos en un encabezado de biblioteca común.

library.h:

#include "module_a.h"
// etc.

De esta manera, un programa que usa la biblioteca solo necesita incluir un archivo con solo:

main.c:

#include "library.h"

int main() {
    //my_private_fn(); // This triggers a compile warning.
    my_public_fn();    // I can still reach the "core" public function.
    module_a_fn();     // This calls the "private" function internally.

    return 0;
}

Compilar gcc -Wall *.c -o main.oy ejecutar ./main.orendimientos:

Public function called.
Private function called.
Public function called.