diff --git a/CMakeLists.txt b/CMakeLists.txt index b130e10d..9a0475d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,9 @@ INCLUDE(GNUInstallDirs) LIST( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ) +# Load C++20 module support +INCLUDE( SIOModules ) + # options OPTION( SIO_EXAMPLES "Switch ON to build SIO examples" ON ) OPTION( INSTALL_DOC "Set to OFF to skip build/install Documentation" OFF ) @@ -38,6 +41,16 @@ ENDIF() # ZLIB package IF( SIO_BUILTIN_ZLIB ) ADD_SUBDIRECTORY( zlib ) + # Install ZLIB when SIO modules are enabled (needed for export) + # Note: This makes ZLIB visible to downstream users as SIO::ZLIB when modules are enabled. + # This is required by CMake's export mechanism for targets with FILE_SET CXX_MODULES. + # The ZLIB target is static and should not conflict with system ZLIB libraries. + IF( SIO_ENABLE_CXX_MODULES ) + INSTALL( TARGETS ZLIB + EXPORT SIOTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + ENDIF() ELSE() FIND_PACKAGE( ZLIB REQUIRED ) ENDIF() diff --git a/cmake/SIOModules.cmake b/cmake/SIOModules.cmake new file mode 100644 index 00000000..01cf44cf --- /dev/null +++ b/cmake/SIOModules.cmake @@ -0,0 +1,84 @@ +#--------------------------------------------------------------------------------------------------- +# CMake support for C++20 modules in SIO +# +# This file provides macros and options for generating and using C++20 module interfaces +# for the SIO library. +#--------------------------------------------------------------------------------------------------- + +# Option to enable C++20 module generation +option(SIO_ENABLE_CXX_MODULES "Generate C++20 module interface files (.ixx) for SIO" OFF) + +# Check if modules are actually supported +if(SIO_ENABLE_CXX_MODULES) + # Check CMake version + if(CMAKE_VERSION VERSION_LESS 3.29) + message(WARNING "C++20 modules require CMake 3.29 or later (found ${CMAKE_VERSION}). Disabling module support.") + set(SIO_ENABLE_CXX_MODULES OFF CACHE BOOL "C++20 modules disabled due to CMake version" FORCE) + endif() + + # Check generator + if(CMAKE_GENERATOR STREQUAL "Unix Makefiles") + message(WARNING "C++20 modules are not supported with the Unix Makefiles generator. Please use Ninja. Disabling module support.") + set(SIO_ENABLE_CXX_MODULES OFF CACHE BOOL "C++20 modules disabled due to generator" FORCE) + endif() + + # Check compiler support + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14.0) + message(WARNING "C++20 modules require GCC 14 or later (found ${CMAKE_CXX_COMPILER_VERSION}). Disabling module support.") + set(SIO_ENABLE_CXX_MODULES OFF CACHE BOOL "C++20 modules disabled due to compiler version" FORCE) + endif() + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18.0) + message(WARNING "C++20 modules require Clang 18 or later (found ${CMAKE_CXX_COMPILER_VERSION}). Disabling module support.") + set(SIO_ENABLE_CXX_MODULES OFF CACHE BOOL "C++20 modules disabled due to compiler version" FORCE) + endif() + else() + message(WARNING "C++20 modules have only been tested with GCC 14+ and Clang 18+. Found ${CMAKE_CXX_COMPILER_ID}. Proceed at your own risk.") + endif() +endif() + +if(SIO_ENABLE_CXX_MODULES) + message(STATUS "C++20 module support is ENABLED for SIO") +else() + message(STATUS "C++20 module support is DISABLED for SIO") +endif() + +#--------------------------------------------------------------------------------------------------- +# SIO_ADD_MODULE_INTERFACE( target module_file ) +# +# Add a C++20 module interface to a SIO target. +# +# This function conditionally adds a module interface file to the specified target when +# SIO_ENABLE_CXX_MODULES is ON. The module interface is added as a PUBLIC CXX_MODULES file set, +# which makes it available for installation and use by downstream projects. +# +# When SIO_ENABLE_CXX_MODULES is OFF, this function returns immediately without any action. +# +# Arguments: +# target - The target to which the module interface should be added +# module_file - The .ixx file implementing the module interface (absolute or relative path) +# +# Example: +# SIO_ADD_MODULE_INTERFACE(sio ${CMAKE_CURRENT_SOURCE_DIR}/sio_module.ixx) +# +# Note: +# The module_file should be installed separately using INSTALL(TARGETS ... FILE_SET CXX_MODULES) +# when SIO_ENABLE_CXX_MODULES is ON. +#--------------------------------------------------------------------------------------------------- +function(SIO_ADD_MODULE_INTERFACE target module_file) + if(NOT SIO_ENABLE_CXX_MODULES) + return() + endif() + + message(STATUS "Adding C++20 module 'sio' to target '${target}'") + + # Add the module file to the target as a CXX_MODULES file set + # Use PUBLIC so the module gets installed and can be used by downstream projects + target_sources(${target} + PUBLIC + FILE_SET CXX_MODULES + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + FILES ${module_file} + ) +endfunction() diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 4333e448..202975cd 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -26,9 +26,25 @@ IF( SIO_MACROS_WITH_EXCEPTION ) TARGET_COMPILE_DEFINITIONS(sio PUBLIC "-DSIO_MACROS_WITH_EXCEPTION=1") ENDIF() -SIO_INSTALL_SHARED_LIBRARY( sio - EXPORT SIOTargets - DESTINATION ${CMAKE_INSTALL_LIBDIR} ) +# Add C++20 module interface if enabled (before install) +IF( SIO_ENABLE_CXX_MODULES ) + SIO_ADD_MODULE_INTERFACE( sio ${CMAKE_CURRENT_SOURCE_DIR}/sio_module.ixx ) + # Install library with module file set + INSTALL( TARGETS sio + EXPORT SIOTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + FILE_SET CXX_MODULES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/sio/modules + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + ) +ELSE() + # Install library without modules + SIO_INSTALL_SHARED_LIBRARY( sio + EXPORT SIOTargets + DESTINATION ${CMAKE_INSTALL_LIBDIR} ) +ENDIF() # install SIO headers SIO_INSTALL_DIRECTORY( include/sio DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) diff --git a/source/include/sio/definitions.h b/source/include/sio/definitions.h index b2921445..c3b285cb 100644 --- a/source/include/sio/definitions.h +++ b/source/include/sio/definitions.h @@ -108,35 +108,35 @@ namespace sio { using block_list = std::vector ; /// The null byte definition - static constexpr byte null_byte = '\0' ; + inline constexpr byte null_byte = '\0' ; - static constexpr byte padding_bytes [4] = {null_byte} ; + inline constexpr byte padding_bytes [4] = {null_byte} ; /// Kilo byte unit - static constexpr std::size_t kbyte = 0x00000400 ; + inline constexpr std::size_t kbyte = 0x00000400 ; /// Mega byte unit - static constexpr std::size_t mbyte = 0x00100000 ; + inline constexpr std::size_t mbyte = 0x00100000 ; /// The compression bit mask - static constexpr unsigned int compression_bit = 0x00000001 ; + inline constexpr unsigned int compression_bit = 0x00000001 ; /// The bit alignment mask - static constexpr unsigned int bit_align = 0x00000003 ; + inline constexpr unsigned int bit_align = 0x00000003 ; /// The additional padding added in buffer IO - static constexpr unsigned int padding = 3 ; + inline constexpr unsigned int padding = 3 ; /// The additional padding mask - static constexpr unsigned int padding_mask = 0xfffffffc ; + inline constexpr unsigned int padding_mask = 0xfffffffc ; /// The record marker - static constexpr unsigned int record_marker = 0xabadcafe ; + inline constexpr unsigned int record_marker = 0xabadcafe ; /// The block marker - static constexpr unsigned int block_marker = 0xdeadbeef ; + inline constexpr unsigned int block_marker = 0xdeadbeef ; /// The maximum length of a record name - static constexpr std::size_t max_record_name_len = 64 ; + inline constexpr std::size_t max_record_name_len = 64 ; /// The maximum length of a record_info in memory - static constexpr std::size_t max_record_info_len = 2*sizeof(sio::ifstream::pos_type) + 5*sizeof(unsigned int) + max_record_name_len ; + inline constexpr std::size_t max_record_info_len = 2*sizeof(sio::ifstream::pos_type) + 5*sizeof(unsigned int) + max_record_name_len ; // TODO: Do we still need all of this ?? - static constexpr std::size_t single_len = 1 ; - static constexpr std::size_t double_len = 2 ; - static constexpr std::size_t quad_len = 4 ; - static constexpr std::size_t octo_len = 8 ; + inline constexpr std::size_t single_len = 1 ; + inline constexpr std::size_t double_len = 2 ; + inline constexpr std::size_t quad_len = 4 ; + inline constexpr std::size_t octo_len = 8 ; /** * @brief record_info struct. diff --git a/source/include/sio/memcpy.h b/source/include/sio/memcpy.h index 93bf79cd..ab03f832 100644 --- a/source/include/sio/memcpy.h +++ b/source/include/sio/memcpy.h @@ -16,13 +16,13 @@ namespace sio { */ template struct sizeof_helper { - static constexpr std::size_t size = sizeof(T) ; + static inline constexpr std::size_t size = sizeof(T) ; }; #define SIO_FORCE_SIZEOF( TYPE, SIZE ) \ template <> \ struct sizeof_helper { \ - static constexpr std::size_t size = SIZE ; \ + static inline constexpr std::size_t size = SIZE ; \ } SIO_FORCE_SIZEOF( char, 1 ) ; diff --git a/source/sio_module.ixx b/source/sio_module.ixx new file mode 100644 index 00000000..948634b7 --- /dev/null +++ b/source/sio_module.ixx @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Module interface for SIO - Simple I/O library +// +// This module exports the core SIO types and interfaces. + +module; + +// Global module fragment - includes go here +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include <_types.h> +#include <_types/_uint16_t.h> +#include <_types/_uint32_t.h> +#include <_types/_uint64_t.h> +#include <_types/_uint8_t.h> +#include +#include +#else +#include +#endif + +// SIO headers +#include "sio/definitions.h" +#include "sio/exception.h" +#include "sio/version.h" +#include "sio/buffer.h" +#include "sio/block.h" +#include "sio/io_device.h" +#include "sio/memcpy.h" +#include "sio/api.h" +#include "sio/compression/zlib.h" + +export module sio; + +// Export SIO namespace and types +export namespace sio { + // Type aliases from definitions.h + using sio::byte; + using sio::byte_array; + using sio::byte_traits; + using sio::index_type; + using sio::options_type; + using sio::version_type; + using sio::ptr_type; + using sio::pointed_at_map; + using sio::pointer_to_map; + using sio::ifstream; + using sio::ofstream; + using sio::fstream; + using sio::block_ptr; + using sio::block_list; + + // Constants from definitions.h + using sio::null_byte; + using sio::padding_bytes; + using sio::kbyte; + using sio::mbyte; + using sio::compression_bit; + using sio::bit_align; + using sio::padding; + using sio::padding_mask; + using sio::record_marker; + using sio::block_marker; + using sio::max_record_name_len; + using sio::max_record_info_len; + using sio::single_len; + using sio::double_len; + using sio::quad_len; + using sio::octo_len; + + // Structs and enums from definitions.h + using sio::record_info; + using sio::validate; + using sio::valid_record_name; + + // Exception types + using sio::exception; + using sio::error_code; + + // Version class + using sio::version; + + // Core classes + using sio::buffer; + using sio::buffer_span; + using sio::block; + using sio::read_device; + using sio::write_device; + + // Memory copy utilities + using sio::memcpy; + using sio::sizeof_helper; + + // API class and functions + using sio::api; + + // Compression + using sio::zlib_compression; +}