Setiap library dimulai dari pertanyaan: fungsi apa yang akan disediakan dan bagaimana pengguna lain akan memakainya? Pada bagian ini, kita rancang header publik examplelib.h sebagai kontrak antara library dan konsumen. Header ini berisi konstanta versi, enum kode error, dan prototype fungsi yang akan kita implementasikan.

Konsep #

Public API adalah permukaan library yang dilihat pengguna. Aturan emas desain API C:

  1. Header bersih — hanya deklarasi (tipe, konstanta, prototype), tidak ada definisi fungsi.
  2. Header guard — #ifndef EXAMPLELIB_H / #define EXAMPLELIB_H / #endif mencegah include ganda.
  3. Stable ABI/API — sekali dirilis, signature fungsi tidak boleh berubah tanpa versi major baru.
  4. Konstanta versi — EXAMPLELIB_VERSION_MAJOR/MINOR/PATCH memungkinkan runtime check.
  5. Kode error sebagai enum — lebih self-documenting daripada integer negatif acak.

Mekanisme kerja: konsumen library meng-include examplelib.h dan memanggil fungsi. Linker menyambungkan call site ke definisi di libexamplelib.a atau libexamplelib.so. Bila API berubah (mis. tambah parameter), binary lama yang sudah di-link dengan versi sebelumnya bisa crash — itulah kenapa API harus stabil.

Implementasi #

Header lengkap examplelib.h:

c
/* examplelib.h - Public API untuk contoh reusable library
 *
 * Versi: 1.0.0 */
#ifndef EXAMPLELIB_H
#define EXAMPLELIB_H

#include <stddef.h>
#include <stdint.h>

#define EXAMPLELIB_VERSION_MAJOR 1
#define EXAMPLELIB_VERSION_MINOR 0
#define EXAMPLELIB_VERSION_PATCH 0
#define EXAMPLELIB_VERSION_STRING "1.0.0"

typedef enum {
    EXLIB_OK            = 0,
    EXLIB_ERR_NULL      = -1,
    EXLIB_ERR_RANGE     = -2,
    EXLIB_ERR_FORMAT    = -3,
    EXLIB_ERR_OVERFLOW  = -4,
    EXLIB_ERR_NOT_FOUND = -5
} exlib_status;

const char *exlib_version(void);
exlib_status exlib_mean(const double *data, size_t n, double *out);
exlib_status exlib_stddev(const double *data, size_t n, double *out);
exlib_status exlib_reverse_bytes(uint8_t *data, size_t n);
exlib_status exlib_format_size(uint64_t bytes, char *out, size_t out_cap);
exlib_status exlib_contains(const char *haystack, const char *needle);
const char *exlib_strerror(exlib_status code);

#endif /* EXAMPLELIB_H */

Demo penggunaan API (mengasumsikan implementasi sudah ada):

c
/* demo_api.c - Penggunaan API examplelib */
#include <stdio.h>
#include "examplelib.h"

int main(void) {
    printf("Versi: %s\n", exlib_version());

    double data[] = {1.0, 2.0, 3.0, 4.0, 5.0};
    double mean;
    exlib_status s = exlib_mean(data, 5, &mean);
    if (s != EXLIB_OK) {
        printf("Error: %s\n", exlib_strerror(s));
        return 1;
    }
    printf("Mean: %.2f\n", mean);

    double sd;
    s = exlib_stddev(data, 5, &sd);
    if (s == EXLIB_OK) {
        printf("Stddev: %.2f\n", sd);
    }

    char buf[32];
    s = exlib_format_size(1572864ULL, buf, sizeof(buf));
    if (s == EXLIB_OK) {
        printf("Size: %s\n", buf);
    }

    s = exlib_contains("hello world", "world");
    printf("Contains 'world': %s\n",
           (s == EXLIB_OK) ? "yes" : exlib_strerror(s));

    return 0;
}

Output yang Dihasilkan:

text
Versi: 1.0.0
Mean: 3.00
Stddev: 1.41
Size: 1.50 MB
Contains 'world': yes

Mari kita telaah baris-baris kode di atas:

  1. Baris 1-3 (komentar header): Komentar singkat yang menjelaskan tujuan file dan versi. Konvensi ini umum di library C (lihat zlib.h, curl.h) — pengguna yang membuka file langsung tahu apa yang dilakukan library dan versi berapa.
  2. Baris 6-7 (#ifndef/#define): Header guard standar. Bila header di-include dua kali dalam satu unit kompilasi (mis. lewat dua header lain), guard mencegah redefinition error.
  3. Baris 9-10 (#include): Hanya include yang dibutuhkan konsumen. <stddef.h> untuk size_t, <stdint.h> untuk uint8_t dan uint64_t. Hindari include yang tidak perlu karena memperlambat kompilasi konsumen.
  4. Baris 12-15 (EXAMPLELIB_VERSION_*): Konstanta versi dipisah menjadi MAJOR/MINOR/PATCH mengikuti Semantic Versioning (https://semver.org ). Konsumen bisa cek #if EXAMPLELIB_VERSION_MAJOR >= 2 untuk fitur versi 2+.
  5. Baris 17-24 (exlib_status enum): Semua fungsi mengembalikan enum ini. Nilai 0 = sukses, negatif = error. Konsumen bisa switch (s) untuk menangani error berbeda.
  6. Baris 26-32 (prototype): Signature fungsi konsisten: exlib_status exlib_xxx(input, ..., output). Input selalu const bila tidak diubah. Output selalu lewat pointer.
  7. Baris 33 (exlib_strerror): Fungsi terjemahan kode error ke string. Konvensi standar sama seperti strerror di <string.h>. Memudahkan logging.

Kode Lengkap #

Kode lengkap untuk examplelib.h ada di direktori include/ proyek. Header ini menjadi kontrak yang tidak boleh berubah kecuali versi major naik.

Pengujian #

Pengujian desain API dilakukan dengan dua cara:

  1. Compile check — pastikan header bisa di-include tanpa error. gcc -std=c11 -Wall -Wextra -c -x c - < examplelib.h harus sukses.
  2. Demo penggunaan — tulis program kecil yang memakai API. Bila API terasa canggung (terlalu banyak parameter, return type membingungkan), pertimbangkan redesign sebelum implementasi.
bash
# Pastikan header bisa di-include standalone
echo '#include "examplelib.h"
int main(void) { return 0; }' > /tmp/header_check.c
gcc -std=c11 -Wall -Wextra -Iinclude -c /tmp/header_check.c -o /tmp/header_check.o