Back to skills
SKILL.md
Lang C Library Dev
ASecurityC 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.
- 8 stars
- 0 votes
- 0 copies
- 0 views
- Added February 8, 2026
Works with
Security analysis
93/100- Performs destructive filesystem operations
npx -y skills add aRustyDev/ai --skill lang-c-library-dev --agent claude-codeAre you the author of Lang C Library Dev?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/arustydev-lang-c-library-dev)---
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
```c
// 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
```
```c
// 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:**
```c
// 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
```
```c
// 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**
```c
// 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:**
```c
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:**
```c
// 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
```c
// 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
```c
// 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
```c
// 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;
```
```bash
# Link with version script
gcc -shared -Wl,--version-script=mylib.map -o libmylib.so *.o
```
---
## Build Systems
### CMake (Modern Approach)
```cmake
# 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
# 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
# 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
# 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
```c
/**
* @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
```c
// 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
```c
// 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)
```c
// 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):**
```bash
# 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):**
```bash
# 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):**
```bash
# Structure
mylib_1.2.3/
├── debian/
│ ├── control
│ ├── rules
│ ├── changelog
│ └── copyright
├── include/
├── src/
└── CMakeLists.txt
# Build package
dpkg-buildpackage -us -uc
```
**Red Hat/Fedora (.rpm):**
```spec
# 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
```c
// 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
```c
// Bad: Pollutes global namespace
int init(void);
void cleanup(void);
// Good: Prefixed
int mylib_init(void);
void mylib_cleanup(void);
```
### 3. Unclear Memory Ownership
```c
// 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
```c
// 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
- `lang-c-dev` - Foundational C programming
- `lang-c-memory-eng` - Advanced memory patterns
- [CMake Documentation](https://cmake.org/documentation/)
- [Meson Build System](https://mesonbuild.com/)
- [Doxygen Manual](https://www.doxygen.nl/manual/)
- [Unity Test Framework](https://github.com/ThrowTheSwitch/Unity)
- [Check Framework](https://libcheck.github.io/check/)
- [Criterion](https://github.com/Snaipe/Criterion)
- [pkg-config Guide](https://people.freedesktop.org/~dbn/pkg-config-guide.html)
Attribution
Comments
Loading comments…