Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for building and installing pure interface libraries. Copy over error_handling library from clp-core #1

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CMake/ystdlib-cpp-config.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# ystdlib-cpp CMake configuration file

@PACKAGE_INIT@

include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")

# TODO: check required dependencies
76 changes: 76 additions & 0 deletions CMake/ystdlib-cpp-helpers.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# check_if_header_only_library()
#
# @param source_list_var The list of source files that a target library uses
# @param is_header_only_var Returns whether the target library only contains header files
function(check_if_header_only_library source_list_var is_header_only_var)
set(local_source_list "${${source_list_var}}")
foreach(src_file IN LISTS local_source_list)
if(${src_file} MATCHES ".*\\.(h|inc)")
list(REMOVE_ITEM local_source_list "${src_file}")
endif()
endforeach()

if(local_source_list STREQUAL "")
set(${is_header_only_var} 1 PARENT_SCOPE)
else()
set(${is_header_only_var} 0 PARENT_SCOPE)
endif()
endfunction()

# ystdlib_cpp_library()
#
# CMake function to imitate Bazel's cc_library rule.
#
# Parameters:
# NAME: name of the target
# HDRS: List of public header files for the library
# SRCS: List of source files for the library
# DEPS: List of other libraries to be linked in to the binary targets
function(ystdlib_cpp_library)
set(options "")
set(oneValueArgs NAME)
set(multiValueArgs HDRS SRCS DEPS)
cmake_parse_arguments(arg_ystdlib_cpp_lib
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN}
)

if(YSTDLIB_CPP_ENABLE_INSTALL)
set(_TARGET_LIB_NAME "${arg_ystdlib_cpp_lib_NAME}")
else()
set(_TARGET_LIB_NAME "ystdlib_${arg_ystdlib_cpp_lib_NAME}")
endif()

check_if_header_only_library(arg_ystdlib_cpp_lib_SRCS _TARGET_LIB_IS_INTERFACE)

if (_TARGET_LIB_IS_INTERFACE)
add_library(${_TARGET_LIB_NAME} INTERFACE)
target_include_directories(${_TARGET_LIB_NAME} INTERFACE
"$<BUILD_INTERFACE:${YSTDLIB_CPP_BUILD_INCLUDE_DIRS}>"
"$<INSTALL_INTERFACE:${YSTDLIB_CPP_INSTALL_INCLUDE_DIRS}>"
)
else()
message(FATAL_ERROR "Non-interface library is currently not supported.")
endif()

target_link_libraries(${_TARGET_LIB_NAME}
PUBLIC ${arg_ystdlib_cpp_lib_DEPS}
)

add_library(ystdlib::${arg_ystdlib_cpp_lib_NAME} ALIAS ${_TARGET_LIB_NAME})

if(YSTDLIB_CPP_ENABLE_INSTALL)
install(
FILES ${arg_ystdlib_cpp_lib_HDRS}
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ystdlib/${arg_ystdlib_cpp_lib_NAME}"
)
install(TARGETS ${_TARGET_LIB_NAME}
EXPORT ${TARGET_EXPORT_NAME}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
endfunction()
77 changes: 77 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
cmake_minimum_required(VERSION 3.16.3)

list(APPEND CMAKE_MODULE_PATH
"${CMAKE_CURRENT_LIST_DIR}/CMake"
)
include(ystdlib-cpp-helpers)

set_property(GLOBAL PROPERTY USE_FOLDERS ON)

# Package information
set(PKG_NAME "ystdlib-cpp")
set(PKG_VERS "0.0.1")
set(PKG_STR "${PKG_NAME}-${PKG_VERS}")
set(TARGET_EXPORT_NAME "${PKG_NAME}-targets")
set(PKG_CONF_NAME "${PKG_NAME}-config")
set(PKG_CONF_VERS_NAME "${PKG_NAME}-config-version")

project(${PKG_NAME} LANGUAGES CXX)

# Static everything for now
option(BUILD_SHARED_LIBS OFF)
option(YSTDLIB_CPP_USE_STATIC_LIBS ON)

if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
message(STATUS "setting C++ standard to C++${CMAKE_CXX_STANDARD}")
endif()

if(NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
option(YSTDLIB_CPP_ENABLE_INSTALL "${PKG_NAME} is the master project. Enable installation." OFF)
else()
option(YSTDLIB_CPP_ENABLE_INSTALL "${PKG_NAME} is the master project. Enable installation." ON)
endif()

# All paths are relative to CMAKE_INSTALL_PREFIX
set(CMAKE_INSTALL_LIBDIR "lib")
set(CMAKE_INSTALL_BINDIR "bin")
set(CMAKE_INSTALL_INCLUDEDIR "include/${PKG_STR}")
set(CMAKE_INSTALL_PKGCONFDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PKG_STR}")

list(APPEND YSTDLIB_CPP_BUILD_INCLUDE_DIRS
"${PROJECT_SOURCE_DIR}"
"${PROJECT_SOURCE_DIR}/submodules"
)

if(YSTDLIB_CPP_ENABLE_INSTALL)
list(APPEND YSTDLIB_CPP_INSTALL_INCLUDE_DIRS ${CMAKE_INSTALL_INCLUDEDIR})
else()
list(APPEND YSTDLIB_CPP_INSTALL_INCLUDE_DIRS ${YSTDLIB_CPP_BUILD_INCLUDE_DIRS})
endif()

add_subdirectory(ystdlib)

if(YSTDLIB_CPP_ENABLE_INSTALL)
include(CMakePackageConfigHelpers)

install(EXPORT ${TARGET_EXPORT_NAME}
FILE ${TARGET_EXPORT_NAME}.cmake
NAMESPACE ystdlib::
DESTINATION ${CMAKE_INSTALL_PKGCONFDIR}
)
configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/CMake/${PKG_CONF_NAME}.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/${PKG_CONF_NAME}.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_PKGCONFDIR}
)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/${PKG_CONF_VERS_NAME}.cmake"
VERSION ${PKG_VERS}
COMPATIBILITY AnyNewerVersion
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/${PKG_CONF_NAME}.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/${PKG_CONF_VERS_NAME}.cmake"
DESTINATION ${CMAKE_INSTALL_PKGCONFDIR}
)
endif()
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ystdlib-cpp
===================================
An open-source C++ library developed and used at YScope.
1 change: 1 addition & 0 deletions ystdlib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_subdirectory(error_handling)
6 changes: 6 additions & 0 deletions ystdlib/error_handling/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ystdlib_cpp_library(
NAME
error_handling
HDRS
"ErrorCode.hpp"
)
150 changes: 150 additions & 0 deletions ystdlib/error_handling/ErrorCode.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#ifndef YSTDLIB_ERROR_HANDLING_ERRORCODE_HPP
#define YSTDLIB_ERROR_HANDLING_ERRORCODE_HPP

#include <concepts>
#include <string>
#include <system_error>
#include <type_traits>

namespace ystdlib::error_handling {
/**
* Concept that defines a template parameter of an integer-based error code enumeration.
* @tparam Type
*/
template <typename Type>
concept ErrorCodeEnumType = std::is_enum_v<Type> && requires(Type type) {
{
static_cast<std::underlying_type_t<Type>>(type)
} -> std::convertible_to<int>;
};

/**
* Template that defines a `std::error_category` of the given set of error code enumeration.
* @tparam ErrorCodeEnum
*/
template <ErrorCodeEnumType ErrorCodeEnum>
class ErrorCategory : public std::error_category {
public:
// Methods implementing `std::error_category`
/**
* Gets the error category name.
* Note: A specialization must be explicitly implemented for each valid `ErrorCodeEnum`.
* @return The name of the error category.
*/
[[nodiscard]] auto name() const noexcept -> char const* override;

/**
* Gets the descriptive message associated with the given error.
* @param error_num
* @return The descriptive message for the error.
*/
[[nodiscard]] auto message(int error_num) const -> std::string override {
return message(static_cast<ErrorCodeEnum>(error_num));
}

/**
* @param error_num
* @param condition
* @return Whether the error condition of the given error matches the given condition.
*/
[[nodiscard]] auto equivalent(
int error_num,
std::error_condition const& condition
) const noexcept -> bool override {
return equivalent(static_cast<ErrorCodeEnum>(error_num), condition);
}

// Methods
/**
* Gets the descriptive message associated with the given error.
* Note: A specialization must be explicitly implemented for each valid `ErrorCodeEnum`.
* @param error_enum.
* @return The descriptive message for the error.
*/
[[nodiscard]] auto message(ErrorCodeEnum error_enum) const -> std::string;

/**
* Note: A specialization can be implemented to create error enum to error condition mappings.
* @param error_num
* @param condition
* @return Whether the error condition of the given error matches the given condition.
*/
[[nodiscard]] auto equivalent(
ErrorCodeEnum error_enum,
std::error_condition const& condition
) const noexcept -> bool;
};

/**
* Template class that defines an error code. An error code is represented by a error enum value and
* the associated error category. This template class is designed to be `std::error_code`
* compatible, meaning that every instance of this class can be used to construct a corresponded
* `std::error_code` instance, or compare with a `std::error_code` instance to inspect a specific
* error.
* @tparam ErrorCodeEnum
*/
template <ErrorCodeEnumType ErrorCodeEnum>
class ErrorCode {
public:
// Constructor
ErrorCode(ErrorCodeEnum error) : m_error{error} {}

/**
* @return The underlying error code enum.
*/
[[nodiscard]] auto get_error() const -> ErrorCodeEnum { return m_error; }

/**
* @return The error code as an error number.
*/
[[nodiscard]] auto get_error_num() const -> int { return static_cast<int>(m_error); }

/**
* @return The reference to the singleton of the corresponded error category.
*/
[[nodiscard]] constexpr static auto get_category() -> ErrorCategory<ErrorCodeEnum> const& {
return cCategory;
}

private:
static inline ErrorCategory<ErrorCodeEnum> const cCategory;

ErrorCodeEnum m_error;
};

/**
* @tparam ErrorCodeEnum
* @param error
* @return Constructed `std::error_code` from the given `ErrorCode` instance.
*/
template <typename ErrorCodeEnum>
[[nodiscard]] auto make_error_code(ErrorCode<ErrorCodeEnum> error) -> std::error_code;

template <ErrorCodeEnumType ErrorCodeEnum>
auto ErrorCategory<ErrorCodeEnum>::equivalent(
ErrorCodeEnum error_enum,
std::error_condition const& condition
) const noexcept -> bool {
return std::error_category::default_error_condition(static_cast<int>(error_enum)) == condition;
}

template <typename ErrorCodeEnum>
auto make_error_code(ErrorCode<ErrorCodeEnum> error) -> std::error_code {
return {error.get_error_num(), ErrorCode<ErrorCodeEnum>::get_category()};
}
} // namespace ystdlib::error_handling

/**
* The macro to create a specialization of `std::is_error_code_enum` for a given type T. Only types
* that are marked with this macro will be considered as a valid YSTDLIB error code enum, and thus
* used to specialize `ErrorCode` and `ErrorCategory` templates.
*/
// NOLINTBEGIN(bugprone-macro-parentheses, cppcoreguidelines-macro-usage)
#define YSTDLIB_ERROR_HANDLING_MARK_AS_ERROR_CODE_ENUM(T) \
template <> \
struct std::is_error_code_enum<ystdlib::error_handling::ErrorCode<T>> : std::true_type { \
static_assert(std::is_enum_v<T>); \
};
// NOLINTEND(bugprone-macro-parentheses, cppcoreguidelines-macro-usage)

#endif // YSTDLIB_ERROR_HANDLING_ERRORCODE_HPP