Adhering to consistent and meaningful coding conventions is crucial for maintaining readable and maintainable code. This document outlines the recommended coding conventions for Apache Celix development, including naming conventions, formatting, comments, control structures, functions and error handling.
Note that not all existing code adheres to these conventions. New code should adhere to these conventions and when possible, existing code should be updated to adhere to these conventions.
camelCase
for variable names.celix_
prefix or celix::
(sub)namespace for global variables.*
and ampersands &
should be placed on the variable type name.snake_case
for structure names._t
postfix for structure typedef.celix_
prefix for structure names.typedef struct celix_<obj> celix_<obj>_t;
celix_
prefix._<obj>_
camelCase infix for the object/module name.camelCase
for the function name.*
should be placed on the variable type name.celix_<obj>_is<Value>
and celix_<obj>_set<Value>
for boolean valuescelix_<obj>_get<Value>
and celix_<obj>_set<Value>
for other valuescelix_<obj>_create
function and destroyed using
a celix_<obj>_destroy
function.celix_<obj>_create
function should return a pointer to the object.celix_<obj>_destroy
function should return a void
and should be able to handle a NULL pointer.
celix_<obj>_destroy
function can be more easily used in
error handling code.Examples:
long celix_bundleContext_installBundle(celix_bundle_context_t* ctx, const char* bundleUrl, bool autoStart)
bool celix_utils_stringEquals(const char* a, const char* b)
celix_status_t celix_utils_createDirectory(const char* path, bool failIfPresent, const char** errorOut)
SNAKE_CASE
for constant names.CELIX_
prefix for constant names.#define
for constants.snake_case
for enum type names.celix_
prefix for enum type names.SNAKE_CASE
for enum value names.CELIX_
prefix for enum value names._e
postfix - for the enumExample:
typedef enum celix_hash_map_key_type {
CELIX_HASH_MAP_STRING_KEY,
CELIX_HASH_MAP_LONG_KEY
} celix_hash_map_key_type_e;
SNAKE_CASE
for macro names.CELIX_
prefix for macro names.snake_case
for file names..h
extension and source files with a .c
extension.include
, api
or spi
directory.private
and src
directory.src
directory.gtest
directory with its own CMakeLists.txt
file and src
directory.celix_
prefix for header file names.snake_case
.celix::
prefixed aliases for the library.celix_
prefix.celix::shell_api
)void* handle;
) and the rest of the members are
function pointers.celix_status_t
and if needed using an out parameter.true
indicates success and false
indicates failure.SNAKE_CASE
, prefixed with CELIX_
and postfixed with _NAME
.SNAKE_CASE
, prefixed with CELIX_
and postfixed with _VERSION
._t
postfixExample:
//celix_foo.h
#include "celix_errno.h"
#define CELIX_FOO_NAME "celix_foo"
#define CELIX_FOO_VERSION 1.0.0
typedef struct celix_foo {
void* handle;
celix_status_t (*doFoo)(void* handle, char** outMsg);
} celix_foo_t;
snake_case
for C bundle target names.celix_
prefix for C bundle target names.celix::
prefixed aliases for C bundle targets.snake_case
for C bundle symbolic names.apache_celix_
prefix for C bundle symbolic names.Apache Celix
prefix for C bundle names.celix_
prefix for C bundle filenames.celix/
for C bundle groups.Examples:
add_celix_bundle(my_bundle
SOURCES src/my_bundle.c
SYMBOLIC_NAME "apache_celix_my_bundle"
NAME "Apache Celix My Bundle"
FILENAME "celix_my_bundle"
VERSION "1.0.0"
GROUP "celix/my_bundle_group"
)
add_library(celix::my_bundle ALIAS my_bundle)
snake_case
for namespace names.celix
namespace.detail
for implementation details.CamelCase
(starting with a capital) for class names.celix::
namespace or sub celix::
namespace.camelCase
for function names.celix::
namespace or sub celix::
namespace.*
and ampersands &
should be placed on the variable type name.SNAKE_CASE
for constants.celix::
namespace or sub celix::
namespace.example:
namespace celix {
constexpr long FRAMEWORK_BUNDLE_ID = 0;
constexpr const char* const SERVICE_ID = "service.id";
}
CamelCase
(starting with a capital) for enum types names.enum class
instead of enum
and if possible use std::int8_t
as base type.SNAKE_CASE
for enum values without a celix/class prefix. Note that for enum values no prefix is required
because enum class values are scoped.Example:
namespace celix {
enum class ServiceRegistrationState {
REGISTERING,
REGISTERED,
UNREGISTERING,
UNREGISTERED
};
}
CamelCase
(starting with a capital) for file names..h
extension and source files with a .cc
extension.celix/Bundle.h
, celix/dm/Component.h
).include
, api
or spi
directory.private
and src
directory.src
directory.#pragma once
header guard.CamelCase
(starting with a capital).celix::
prefixed aliases for the library.celix::Promises
and celix::PushStreams
which requires C++17.Celix::framework
) and the Apache Celix utils library (Celix::utils
) can only
use header-only C++ files. This ensure that the framework and utils library can be used in C only projects and do
not introduce a C++ ABI.celix_
prefix.CamelCase
(starting with a capital) for service names.celix::
namespace or sub celix::
namespace.static constexpr const char* const NAME
to the service class, for the service name.static constexpr const char* const VERSION
to the service class, for the service version.CamelCase
for C++ bundle target names.Celix
prefix for C++ bundle target names.celix::
prefixed aliases for C++ bundle targets.CamelCase
for C++ bundle symbolic names.Apache_Celix_
prefix for C++ bundle symbolic names.Apache Celix
prefix for C++ bundle names.celix_
prefix for C++ bundle filenames.celix/
for C++ bundle groups.Examples:
add_celix_bundle(MyBundle
SOURCES src/MyBundle.cc
SYMBOLIC_NAME "Apache_Celix_MyBundle"
NAME "Apache Celix My Bundle"
FILENAME "celix_MyBundle"
VERSION "1.0.0"
GROUP "celix/MyBundleGroup"
)
add_library(celix::MyBundle ALIAS MyBundle)
TestSuite
postfix..cc
extension.CamelCase
(starting with a capital) and have a Test
postfix.error_injector
libraries) a separate test suite should be used.
ErrorInjectionTestSuite
postfix should be used for the test fixture.TearDown
function or destructor of the test fixture.@
instead of \
for doxygen commands.@brief
command and a short description.@param
commands also provide in, out, or in/out information.@return
commands also provide a description of the return value.@section errors_section Errors
) to document the
possible errors. Use man 2 write
as an example for a good errors section.if
, for
, while
, etc.) that are followed by a parenthesis.else
, do
, etc) that are followed by a brace.clang-format -i <file>
.if
, else if
, and else
statements to handle multiple conditions.switch
statements for multiple conditions with a default case.while
statements for loops that may not execute.do
/while
statements for loops that must execute at least once.for
statements for loops with a known number of iterations.goto
is not allowed, except for error handling in C (for C++ use RAII).goto
statements.
CELIX_DO_IF
, CELIX_GOTO_IF_NULL
and CELIX_GOTO_IF_ERR
macros can also be used.celix_status_t
and if needed using an out parameter.true
indicates success and false
indicates failure.celix_err
functionality.For log levels use the following guidelines:
Example of error handling and logging:
celix_foo_t* celix_foo_create(celix_log_helper_t* logHelper) {
celix_foo_t* foo = calloc(1, sizeof(*foo));
if (!foo) {
goto create_enomem_err;
}
CELIX_GOTO_IF_ERR(create_mutex_err, celixThreadMutex_create(&foo->mutex, NULL));
foo->list = celix_arrayList_create();
foo->map = celix_longHashMap_create();
if (!foo->list || !foo->map) {
goto create_enomem_err;
}
return foo;
create_mutex_err:
celix_logHelper_log(logHelper, CELIX_LOG_LEVEL_ERROR, "Error creating mutex");
free(foo); //mutex not created, do not use celix_foo_destroy to prevent mutex destroy
return NULL;
create_enomem_err:
celix_logHelper_log(logHelper, CELIX_LOG_LEVEL_ERROR, "Error creating foo, out of memory");
celix_foo_destroy(foo); //note celix_foo_destroy can handle NULL
return NULL;
}
void celix_foo_destroy(celix_foo_t* foo) {
if (foo != NULL) {
//note reverse order of creation
celixThreadMutex_destroy(&foo->mutex);
celix_arrayList_destroy(foo->list);
celix_longHashMap_destroy(foo->map);
free(foo);
}
}
EI_TESTS
cmake condition.TearDown
function or destructor of the test fixture._cut
postfix.set(MY_LIB_SOURCES ...)
set(MY_LIB_PUBLIC_LIBS ...)
set(MY_LIB_PRIVATE_LIBS ...)
add_library(my_lib SHARED ${MY_LIB_SOURCES})
target_link_libraries(my_lib PUBLIC ${MY_LIB_PUBLIC_LIBS} PRIVATE ${MY_LIB_PRIVATE_LIBS})
celix_target_hide_symbols(my_lib)
...
if (ENABLE_TESTING)
add_library(my_lib_cut STATIC ${MY_LIB_SOURCES})
target_link_libraries(my_lib_cut PUBLIC ${MY_LIB_PUBLIC_LIBS} ${MY_LIB_PRIVATE_LIBS})
target_include_directories(my_lib_cut PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src
${CMAKE_CURRENT_LIST_DIR}/include
${CMAKE_BINARY_DIR}/celix/gen/includes/my_lib
)
endif ()
celix::Promises
and celix::PushStreams
which requires C++17.celix::framework
and celix::utils
must be header-only.For C and C++ shared libraries, the following target properties should be set:
VERSION
should be set to the library version.SOVERSION
should be set to the library major version.OUTPUT_NAME
should be set to the library name and should contain a celix_
prefix.add_library(my_lib SHARED
src/my_lib.c)
set_target_properties(my_lib
PROPERTIES
VERSION 1.0.0
SOVERSION 1
OUTPUT_NAME celix_my_lib)
For C and C++ static libraries, the following target properties should be set:
POSITION_INDEPENDENT_CODE
should be set to ON
for static libraries.OUTPUT_NAME
should be set to the library name and should contain a celix_
prefix.add_library(my_lib STATIC
src/my_lib.c)
set_target_properties(my_lib
PROPERTIES
POSITION_INDEPENDENT_CODE ON
OUTPUT_NAME celix_my_lib)
For Apache Celix shared libraries, symbol visibility should be configured using the CMake target
properties C_VISIBILITY_PRESET
, CXX_VISIBILITY_PRESET
and VISIBILITY_INLINES_HIDDEN
and a generated export
header.
The C_VISIBILITY_PRESET
and CXX_VISIBILITY_PRESET
target properties can be used to configure the default visibility
of symbols in C and C++ code. The VISIBILITY_INLINES_HIDDEN
property can be used to configure the visibility of
inline functions. The VISIBILITY_INLINES_HIDDEN
property is only supported for C++ code.
The default visibility should be configured to hidden and symbols should be explicitly exported using the export
marcos from a generated export header. The export header can be generated using the CMake function
generate_export_header
. Every library should have its own export header.
For shared libraries, this can be done using the following CMake code:
add_library(my_lib SHARED
src/my_lib.c)
set_target_properties(my_lib PROPERTIES
C_VISIBILITY_PRESET hidden
#For C++ shared libraries also configure CXX_VISIBILITY_PRESET
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
OUTPUT_NAME celix_my_lib)
target_include_directories(my_lib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/celix/gen/includes/my_lib>
PRIVATE
src)
#generate export header
generate_export_header(my_lib
BASE_NAME "CELIX_MY_LIB"
EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/celix/gen/includes/my_lib/celix_my_lib_export.h")
#install
install(TARGETS my_lib EXPORT celix LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/celix_my_lib)
install(DIRECTORY include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/celix_my_lib)
install(DIRECTORY ${CMAKE_BINARY_DIR}/celix/gen/includes/my_lib/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/celix_my_lib)
For bundle, symbol visibility will default be configured to hidden. This can be default by providing
the DO_NOT_CONFIGURE_SYMBOL_VISIBILITY
option to the CMake add_celix_bundle
function.
If symbol visibility is not configured in the add_celix_bundle
, symbol visibility should be configured the same
way as a shared library.
add_celix_bundle(my_bundle
SOURCES src/my_bundle.c
SYMBOLIC_NAME "apache_celix_my_bundle"
NAME "Apache Celix My Bundle"
FILENAME "celix_my_bundle"
VERSION "1.0.0"
GROUP "celix/my_bundle_group"
)
add_library(celix::my_bundle ALIAS my_bundle)
feature/
, hotfix branches with hotfix/
, bugfix branches with bugfix/
and release branches with release/
.feature/1234-add-feature
.