Claude Code Plugins

Community-maintained marketplace

Feedback

lang-c-library-dev

@aRustyDev/ai
0
0

C library development patterns covering API design, header organization, memory management for libraries, ABI stability, build system integration, documentation with Doxygen, testing frameworks, and packaging. Use when creating C libraries, designing public APIs, managing build systems (CMake, Make, Meson), or distributing C packages. Extends lang-c-dev with library-specific tooling and patterns.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name lang-c-library-dev
description C library development patterns covering API design, header organization, memory management for libraries, ABI stability, build system integration, documentation with Doxygen, testing frameworks, and packaging. Use when creating C libraries, designing public APIs, managing build systems (CMake, Make, Meson), or distributing C packages. Extends lang-c-dev with library-specific tooling and patterns.

C Library Development

C-specific patterns for library development. This skill extends lang-c-dev with library design patterns, build systems, ABI stability, and packaging practices.

This Skill Extends

  • lang-c-dev - Foundational C programming (type system, memory, pointers, preprocessor)

For general concepts like type system, pointers, memory management basics, and preprocessor directives, see the foundational skill first.

This Skill Adds

  • Library design: API design patterns, header organization, ABI stability
  • Build systems: CMake, Make, Meson, Autotools, pkg-config
  • Documentation: Doxygen, man pages, API documentation
  • Testing: Unity, Check, CUnit, Criterion testing frameworks
  • Packaging: Static/shared libraries, versioning, distribution

This Skill Does NOT Cover

  • General C programming - see lang-c-dev
  • Advanced memory engineering - see lang-c-memory-eng
  • POSIX APIs and system calls - see lang-c-posix-dev
  • Systems programming patterns - see lang-c-systems-eng
  • Embedded programming - see lang-c-embedded-dev

Quick Reference

Task Command/Pattern
Create static library ar rcs libname.a obj1.o obj2.o
Create shared library gcc -shared -o libname.so obj1.o obj2.o
Install library make install or cmake --install
Generate docs doxygen Doxyfile
Run tests make test or ctest
Check ABI abidiff lib-v1.so lib-v2.so
Package config pkg-config --cflags --libs mylib

Library Design Patterns

Opaque Pointer Pattern (PIMPL)

Purpose: Hide implementation details, maintain ABI stability

// mylib.h (public API)
#ifndef MYLIB_H
#define MYLIB_H

#include <stddef.h>

// Opaque pointer - users cannot see internal structure
typedef struct mylib_context mylib_context_t;

// Constructor/destructor
mylib_context_t* mylib_create(void);
void mylib_destroy(mylib_context_t* ctx);

// Operations
int mylib_process(mylib_context_t* ctx, const char* input, char* output, size_t output_size);
int mylib_set_option(mylib_context_t* ctx, const char* key, const char* value);

#endif // MYLIB_H
// mylib.c (implementation)
#include "mylib.h"
#include <stdlib.h>
#include <string.h>

// Full structure definition hidden from users
struct mylib_context {
    char* buffer;
    size_t buffer_size;
    int flags;
    void* internal_state;
};

mylib_context_t* mylib_create(void) {
    mylib_context_t* ctx = calloc(1, sizeof(*ctx));
    if (!ctx) return NULL;

    ctx->buffer_size = 4096;
    ctx->buffer = malloc(ctx->buffer_size);
    if (!ctx->buffer) {
        free(ctx);
        return NULL;
    }

    return ctx;
}

void mylib_destroy(mylib_context_t* ctx) {
    if (ctx) {
        free(ctx->buffer);
        free(ctx);
    }
}

int mylib_process(mylib_context_t* ctx, const char* input, char* output, size_t output_size) {
    if (!ctx || !input || !output) return -1;
    // Implementation...
    return 0;
}

Error Handling Patterns

Return codes with error context:

// error.h
#ifndef MYLIB_ERROR_H
#define MYLIB_ERROR_H

// Error codes
typedef enum {
    MYLIB_OK = 0,
    MYLIB_ERR_INVALID_ARGUMENT = -1,
    MYLIB_ERR_OUT_OF_MEMORY = -2,
    MYLIB_ERR_IO = -3,
    MYLIB_ERR_PARSE = -4,
} mylib_error_t;

// Get human-readable error message
const char* mylib_strerror(mylib_error_t error);

// Get last error for a context (thread-safe)
mylib_error_t mylib_last_error(mylib_context_t* ctx);
void mylib_clear_error(mylib_context_t* ctx);

#endif
// error.c
#include "error.h"

const char* mylib_strerror(mylib_error_t error) {
    switch (error) {
        case MYLIB_OK: return "Success";
        case MYLIB_ERR_INVALID_ARGUMENT: return "Invalid argument";
        case MYLIB_ERR_OUT_OF_MEMORY: return "Out of memory";
        case MYLIB_ERR_IO: return "I/O error";
        case MYLIB_ERR_PARSE: return "Parse error";
        default: return "Unknown error";
    }
}

Memory Management for Libraries

Rule 1: Who allocates, who frees

// Pattern 1: Library allocates and frees
typedef struct mylib_result mylib_result_t;

mylib_result_t* mylib_compute(const char* input);
void mylib_result_free(mylib_result_t* result);

// Pattern 2: Caller allocates, library fills
int mylib_compute_inplace(const char* input, char* output, size_t* output_size);

// Pattern 3: Callback for custom allocation
typedef void* (*mylib_alloc_fn)(size_t size);
typedef void (*mylib_free_fn)(void* ptr);

void mylib_set_allocator(mylib_alloc_fn alloc, mylib_free_fn free);

Pattern 2 example:

int mylib_get_info(mylib_context_t* ctx, char* buffer, size_t* buffer_size) {
    if (!ctx || !buffer_size) return MYLIB_ERR_INVALID_ARGUMENT;

    // Get required size
    size_t required = calculate_info_size(ctx);

    // If buffer is NULL or too small, return required size
    if (!buffer || *buffer_size < required) {
        *buffer_size = required;
        return MYLIB_ERR_INVALID_ARGUMENT;
    }

    // Fill buffer
    fill_info(ctx, buffer, *buffer_size);
    *buffer_size = required;
    return MYLIB_OK;
}

Namespace Prefixing

All public symbols must be prefixed:

// Good: All symbols prefixed with mylib_
mylib_context_t* mylib_create(void);
typedef enum { MYLIB_OK, MYLIB_ERR } mylib_error_t;
#define MYLIB_VERSION "1.0.0"

// Bad: Pollutes global namespace
context_t* create(void);
typedef enum { OK, ERR } error_t;
#define VERSION "1.0.0"

Header File Organization

Public vs Private Headers

include/
├── mylib/              # Public API
│   ├── mylib.h         # Main header
│   ├── error.h         # Error types
│   └── types.h         # Public types
src/
├── internal.h          # Private API
├── mylib.c
├── error.c
└── internal.c

Header Best Practices

// mylib.h
#ifndef MYLIB_H
#define MYLIB_H

// Include guards always (or #pragma once)

// System headers first
#include <stddef.h>
#include <stdint.h>

// Library version
#define MYLIB_VERSION_MAJOR 1
#define MYLIB_VERSION_MINOR 2
#define MYLIB_VERSION_PATCH 3

// Visibility macros
#ifdef _WIN32
    #ifdef MYLIB_BUILDING
        #define MYLIB_API __declspec(dllexport)
    #else
        #define MYLIB_API __declspec(dllimport)
    #endif
#else
    #define MYLIB_API __attribute__((visibility("default")))
#endif

// C++ compatibility
#ifdef __cplusplus
extern "C" {
#endif

// Forward declarations
typedef struct mylib_context mylib_context_t;

// API declarations
MYLIB_API mylib_context_t* mylib_create(void);
MYLIB_API void mylib_destroy(mylib_context_t* ctx);

#ifdef __cplusplus
}
#endif

#endif // MYLIB_H

Feature Detection Headers

// config.h (generated by build system)
#ifndef MYLIB_CONFIG_H
#define MYLIB_CONFIG_H

// Feature detection
#define MYLIB_HAVE_THREADS 1
#define MYLIB_HAVE_ZLIB 1
// #undef MYLIB_HAVE_OPENSSL

// Platform detection
#ifdef _WIN32
    #define MYLIB_PLATFORM_WINDOWS
#elif defined(__APPLE__)
    #define MYLIB_PLATFORM_MACOS
#elif defined(__linux__)
    #define MYLIB_PLATFORM_LINUX
#endif

#endif

ABI Stability

Versioning Strategy

Semantic Versioning for ABI:

  • Major: Breaking ABI changes
  • Minor: New features, ABI compatible
  • Patch: Bug fixes, ABI compatible

ABI-Safe Changes

Safe (ABI compatible):

  • Adding new functions
  • Adding new struct members at the end (if opaque)
  • Adding new enum values (if used with non_exhaustive pattern)
  • Increasing struct size (if opaque)

Unsafe (ABI breaking):

  • Removing functions
  • Changing function signatures
  • Reordering struct members
  • Changing struct sizes (if exposed)
  • Changing enum underlying type

Symbol Versioning

// Use symbol versioning for evolving APIs
__asm__(".symver mylib_open_v1, mylib_open@MYLIB_1.0");
__asm__(".symver mylib_open_v2, mylib_open@@MYLIB_2.0");

// Old version
int mylib_open_v1(const char* path);

// New version (default)
int mylib_open_v2(const char* path, int flags);

Version Script (Linux)

# mylib.map
MYLIB_1.0 {
    global:
        mylib_create;
        mylib_destroy;
        mylib_process;
    local:
        *;
};

MYLIB_2.0 {
    global:
        mylib_process_v2;
} MYLIB_1.0;
# Link with version script
gcc -shared -Wl,--version-script=mylib.map -o libmylib.so *.o

Build Systems

CMake (Modern Approach)

# CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(mylib VERSION 1.2.3 LANGUAGES C)

# Options
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
option(MYLIB_BUILD_TESTS "Build tests" ON)
option(MYLIB_BUILD_DOCS "Build documentation" OFF)

# Library target
add_library(mylib
    src/mylib.c
    src/error.c
    src/internal.c
)

# Include directories
target_include_directories(mylib
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:include>
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/src
)

# Compiler features
target_compile_features(mylib PRIVATE c_std_99)
target_compile_options(mylib PRIVATE
    $<$<C_COMPILER_ID:GNU,Clang>:-Wall -Wextra -pedantic>
    $<$<C_COMPILER_ID:MSVC>:/W4>
)

# Symbol visibility
set_target_properties(mylib PROPERTIES
    C_VISIBILITY_PRESET hidden
    VERSION ${PROJECT_VERSION}
    SOVERSION ${PROJECT_VERSION_MAJOR}
)

# Dependencies
find_package(ZLIB)
if(ZLIB_FOUND)
    target_link_libraries(mylib PRIVATE ZLIB::ZLIB)
    target_compile_definitions(mylib PRIVATE MYLIB_HAVE_ZLIB)
endif()

# Installation
include(GNUInstallDirs)
install(TARGETS mylib
    EXPORT mylibTargets
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

install(DIRECTORY include/mylib
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

# Export targets
install(EXPORT mylibTargets
    FILE mylibTargets.cmake
    NAMESPACE mylib::
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mylib
)

# Generate config files
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
    "${CMAKE_CURRENT_BINARY_DIR}/mylibConfigVersion.cmake"
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY SameMajorVersion
)

configure_package_config_file(
    "${CMAKE_CURRENT_SOURCE_DIR}/cmake/mylibConfig.cmake.in"
    "${CMAKE_CURRENT_BINARY_DIR}/mylibConfig.cmake"
    INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mylib
)

install(FILES
    "${CMAKE_CURRENT_BINARY_DIR}/mylibConfig.cmake"
    "${CMAKE_CURRENT_BINARY_DIR}/mylibConfigVersion.cmake"
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mylib
)

# Testing
if(MYLIB_BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()

Makefile (Traditional Approach)

# Makefile
PREFIX ?= /usr/local
LIBDIR ?= $(PREFIX)/lib
INCLUDEDIR ?= $(PREFIX)/include

CC = gcc
CFLAGS = -Wall -Wextra -std=c99 -O2 -fPIC
LDFLAGS = -shared

# Library name and version
LIB_NAME = mylib
LIB_VERSION = 1.2.3
LIB_SOVERSION = 1

# Source files
SRCS = src/mylib.c src/error.c src/internal.c
OBJS = $(SRCS:.c=.o)
HEADERS = include/mylib/mylib.h include/mylib/error.h

# Targets
STATIC_LIB = lib$(LIB_NAME).a
SHARED_LIB = lib$(LIB_NAME).so.$(LIB_VERSION)
SHARED_LIB_LINK = lib$(LIB_NAME).so
SHARED_LIB_SONAME = lib$(LIB_NAME).so.$(LIB_SOVERSION)

.PHONY: all clean install uninstall test

all: $(STATIC_LIB) $(SHARED_LIB)

# Static library
$(STATIC_LIB): $(OBJS)
	ar rcs $@ $^

# Shared library
$(SHARED_LIB): $(OBJS)
	$(CC) $(LDFLAGS) -Wl,-soname,$(SHARED_LIB_SONAME) -o $@ $^
	ln -sf $(SHARED_LIB) $(SHARED_LIB_SONAME)
	ln -sf $(SHARED_LIB_SONAME) $(SHARED_LIB_LINK)

# Object files
%.o: %.c
	$(CC) $(CFLAGS) -Iinclude -c -o $@ $<

# Install
install: all
	install -d $(DESTDIR)$(LIBDIR)
	install -m 644 $(STATIC_LIB) $(DESTDIR)$(LIBDIR)/
	install -m 755 $(SHARED_LIB) $(DESTDIR)$(LIBDIR)/
	ln -sf $(SHARED_LIB) $(DESTDIR)$(LIBDIR)/$(SHARED_LIB_SONAME)
	ln -sf $(SHARED_LIB_SONAME) $(DESTDIR)$(LIBDIR)/$(SHARED_LIB_LINK)
	install -d $(DESTDIR)$(INCLUDEDIR)/mylib
	install -m 644 $(HEADERS) $(DESTDIR)$(INCLUDEDIR)/mylib/
	ldconfig -n $(DESTDIR)$(LIBDIR)

# Uninstall
uninstall:
	rm -f $(DESTDIR)$(LIBDIR)/$(STATIC_LIB)
	rm -f $(DESTDIR)$(LIBDIR)/$(SHARED_LIB)*
	rm -rf $(DESTDIR)$(INCLUDEDIR)/mylib

# Clean
clean:
	rm -f $(OBJS) $(STATIC_LIB) $(SHARED_LIB)*

# Tests
test:
	$(MAKE) -C tests

Meson (Modern Alternative)

# meson.build
project('mylib', 'c',
  version: '1.2.3',
  default_options: ['c_std=c99', 'warning_level=3']
)

# Dependencies
zlib_dep = dependency('zlib', required: false)

# Configuration
conf_data = configuration_data()
conf_data.set('MYLIB_VERSION', meson.project_version())
conf_data.set('MYLIB_HAVE_ZLIB', zlib_dep.found())

configure_file(
  input: 'config.h.in',
  output: 'config.h',
  configuration: conf_data
)

# Library
mylib_sources = files(
  'src/mylib.c',
  'src/error.c',
  'src/internal.c'
)

mylib_inc = include_directories('include')

mylib = library('mylib',
  mylib_sources,
  include_directories: mylib_inc,
  dependencies: zlib_dep,
  version: meson.project_version(),
  soversion: '1',
  install: true
)

# Install headers
install_headers(
  'include/mylib/mylib.h',
  'include/mylib/error.h',
  subdir: 'mylib'
)

# pkg-config
pkg = import('pkgconfig')
pkg.generate(mylib,
  description: 'My awesome C library',
  subdirs: 'mylib'
)

# Testing
if get_option('build_tests')
  subdir('tests')
endif

pkg-config File

# mylib.pc.in
prefix=@PREFIX@
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include

Name: mylib
Description: My awesome C library
Version: @VERSION@
Libs: -L${libdir} -lmylib
Cflags: -I${includedir}
Requires: zlib

Documentation with Doxygen

Doxygen Configuration

# Doxyfile
PROJECT_NAME           = "mylib"
PROJECT_NUMBER         = 1.2.3
OUTPUT_DIRECTORY       = docs
GENERATE_HTML          = YES
GENERATE_LATEX         = NO
EXTRACT_ALL            = YES
EXTRACT_PRIVATE        = NO
EXTRACT_STATIC         = NO
INPUT                  = include/mylib
RECURSIVE              = YES
USE_MDFILE_AS_MAINPAGE = README.md

Documentation Patterns

/**
 * @file mylib.h
 * @brief Main library interface
 * @author Your Name
 */

/**
 * @brief Context object for library operations
 *
 * This is an opaque type. Users should not access its members directly.
 * Use the provided API functions to interact with it.
 */
typedef struct mylib_context mylib_context_t;

/**
 * @brief Create a new library context
 *
 * @return Pointer to new context, or NULL on allocation failure
 * @note The returned context must be freed with mylib_destroy()
 * @see mylib_destroy()
 */
MYLIB_API mylib_context_t* mylib_create(void);

/**
 * @brief Destroy a library context
 *
 * @param ctx Context to destroy (may be NULL)
 * @note Safe to call with NULL pointer
 * @warning Do not use the context after calling this function
 */
MYLIB_API void mylib_destroy(mylib_context_t* ctx);

/**
 * @brief Process input data
 *
 * @param ctx Library context
 * @param input Input string (must be null-terminated)
 * @param output Output buffer
 * @param output_size Size of output buffer
 * @return 0 on success, negative error code on failure
 * @retval MYLIB_OK Success
 * @retval MYLIB_ERR_INVALID_ARGUMENT Invalid parameter
 * @retval MYLIB_ERR_OUT_OF_MEMORY Allocation failed
 *
 * @code{.c}
 * mylib_context_t* ctx = mylib_create();
 * char output[1024];
 * int result = mylib_process(ctx, "input", output, sizeof(output));
 * if (result == MYLIB_OK) {
 *     printf("Output: %s\n", output);
 * }
 * mylib_destroy(ctx);
 * @endcode
 */
MYLIB_API int mylib_process(mylib_context_t* ctx, const char* input,
                            char* output, size_t output_size);

Testing Frameworks

Unity Test Framework

// test_mylib.c
#include "unity.h"
#include "mylib/mylib.h"

void setUp(void) {
    // Run before each test
}

void tearDown(void) {
    // Run after each test
}

void test_create_destroy(void) {
    mylib_context_t* ctx = mylib_create();
    TEST_ASSERT_NOT_NULL(ctx);
    mylib_destroy(ctx);
}

void test_process_valid_input(void) {
    mylib_context_t* ctx = mylib_create();
    char output[1024];

    int result = mylib_process(ctx, "test input", output, sizeof(output));
    TEST_ASSERT_EQUAL(MYLIB_OK, result);
    TEST_ASSERT_EQUAL_STRING("expected output", output);

    mylib_destroy(ctx);
}

void test_process_invalid_argument(void) {
    int result = mylib_process(NULL, "input", NULL, 0);
    TEST_ASSERT_EQUAL(MYLIB_ERR_INVALID_ARGUMENT, result);
}

int main(void) {
    UNITY_BEGIN();
    RUN_TEST(test_create_destroy);
    RUN_TEST(test_process_valid_input);
    RUN_TEST(test_process_invalid_argument);
    return UNITY_END();
}

Check Framework

// test_check.c
#include <check.h>
#include "mylib/mylib.h"

START_TEST(test_create)
{
    mylib_context_t* ctx = mylib_create();
    ck_assert_ptr_nonnull(ctx);
    mylib_destroy(ctx);
}
END_TEST

START_TEST(test_process)
{
    mylib_context_t* ctx = mylib_create();
    char output[1024];

    int result = mylib_process(ctx, "input", output, sizeof(output));
    ck_assert_int_eq(result, MYLIB_OK);

    mylib_destroy(ctx);
}
END_TEST

Suite* mylib_suite(void) {
    Suite* s = suite_create("mylib");
    TCase* tc_core = tcase_create("Core");

    tcase_add_test(tc_core, test_create);
    tcase_add_test(tc_core, test_process);
    suite_add_tcase(s, tc_core);

    return s;
}

int main(void) {
    int number_failed;
    Suite* s = mylib_suite();
    SRunner* sr = srunner_create(s);

    srunner_run_all(sr, CK_NORMAL);
    number_failed = srunner_ntests_failed(sr);
    srunner_free(sr);

    return (number_failed == 0) ? 0 : 1;
}

Criterion (Modern Framework)

// test_criterion.c
#include <criterion/criterion.h>
#include "mylib/mylib.h"

Test(mylib, create_destroy) {
    mylib_context_t* ctx = mylib_create();
    cr_assert_not_null(ctx);
    mylib_destroy(ctx);
}

Test(mylib, process_valid) {
    mylib_context_t* ctx = mylib_create();
    char output[1024];

    int result = mylib_process(ctx, "input", output, sizeof(output));
    cr_assert_eq(result, MYLIB_OK);

    mylib_destroy(ctx);
}

Test(mylib, process_null_context) {
    char output[1024];
    int result = mylib_process(NULL, "input", output, sizeof(output));
    cr_assert_eq(result, MYLIB_ERR_INVALID_ARGUMENT);
}

Packaging and Distribution

Static vs Shared Libraries

Static Library (.a):

# Compile object files
gcc -c -I include src/mylib.c -o mylib.o
gcc -c -I include src/error.c -o error.o

# Create archive
ar rcs libmylib.a mylib.o error.o

# Link with static library
gcc main.c -L. -lmylib -o program

Shared Library (.so / .dylib / .dll):

# Compile with position-independent code
gcc -fPIC -c -I include src/mylib.c -o mylib.o
gcc -fPIC -c -I include src/error.c -o error.o

# Create shared library (Linux)
gcc -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1.2.3 mylib.o error.o

# Create symlinks
ln -s libmylib.so.1.2.3 libmylib.so.1
ln -s libmylib.so.1 libmylib.so

# Link with shared library
gcc main.c -L. -lmylib -o program

Versioning Scheme

Linux:

  • libmylib.so.1.2.3 - Real file with full version
  • libmylib.so.1 - Symlink (SONAME) for ABI compatibility
  • libmylib.so - Symlink for linker

macOS:

  • libmylib.1.2.3.dylib - Real file
  • libmylib.1.dylib - Symlink
  • libmylib.dylib - Symlink

Distribution Packages

Debian/Ubuntu (.deb):

# Structure
mylib_1.2.3/
├── debian/
│   ├── control
│   ├── rules
│   ├── changelog
│   └── copyright
├── include/
├── src/
└── CMakeLists.txt

# Build package
dpkg-buildpackage -us -uc

Red Hat/Fedora (.rpm):

# mylib.spec
Name:           mylib
Version:        1.2.3
Release:        1%{?dist}
Summary:        My awesome C library

License:        MIT
URL:            https://github.com/username/mylib
Source0:        %{name}-%{version}.tar.gz

BuildRequires:  gcc, cmake, zlib-devel
Requires:       zlib

%description
My awesome C library for doing awesome things.

%prep
%setup -q

%build
%cmake
%cmake_build

%install
%cmake_install

%files
%license LICENSE
%doc README.md
%{_libdir}/libmylib.so.*
%{_libdir}/libmylib.a
%{_includedir}/mylib/

%changelog
* Mon Jan 01 2024 Your Name <email@example.com> - 1.2.3-1
- Initial package

Best Practices Checklist

  • All public symbols have library prefix (mylib_)
  • Header files have include guards or #pragma once
  • C++ compatibility with extern "C"
  • Symbol visibility controlled (hidden by default)
  • Opaque pointers for implementation hiding
  • Clear memory ownership rules
  • Consistent error handling pattern
  • Thread-safety documented
  • ABI versioning strategy
  • Comprehensive API documentation
  • Unit tests with good coverage
  • pkg-config file provided
  • CMake config files generated
  • Build works on multiple platforms
  • No warnings with -Wall -Wextra -pedantic

Common Pitfalls

1. Exposing Internal Types

// Bad: Exposes internal implementation
typedef struct {
    char* buffer;
    size_t size;
} mylib_context_t;

// Good: Opaque pointer
typedef struct mylib_context mylib_context_t;

2. No Namespace Prefixing

// Bad: Pollutes global namespace
int init(void);
void cleanup(void);

// Good: Prefixed
int mylib_init(void);
void mylib_cleanup(void);

3. Unclear Memory Ownership

// Bad: Who frees the result?
char* mylib_process(const char* input);

// Good: Clear ownership
char* mylib_process(const char* input);  // Caller frees
void mylib_result_free(char* result);

4. Breaking ABI

// v1.0.0
int mylib_func(int a);

// v1.1.0 - WRONG! ABI break
int mylib_func(int a, int b);

// v1.1.0 - Correct: Add new function
int mylib_func(int a);
int mylib_func_ex(int a, int b);

References