diff --git a/.clang-format b/.clang-format index 1dd1f90..d6da695 100644 --- a/.clang-format +++ b/.clang-format @@ -12,7 +12,7 @@ BraceWrapping: AfterExternBlock: 'true' BeforeCatch: 'true' -ColumnLimit: 125 +ColumnLimit: 100 BreakInheritanceList: AfterColon CompactNamespaces: 'false' ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' diff --git a/.gitignore b/.gitignore index 616ca92..a0acb2d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ -/.idea -/cmake-build-debug -/.git \ No newline at end of file +cmake-build-debug/ +.git/ +tests/ +.vs/ +out/ +/out +/build +build/ +.idea/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 17d7da3..4ef99ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,42 @@ cmake_minimum_required(VERSION 3.16.5) -project(PeachLibrary) +project(peach_library + VERSION 0.1 + LANGUAGES CXX +) -set(CMAKE_CXX_STANDARD 20) +option(COMPONENT_TARGETS_ENABLED "enable individual config targets" YES) -add_library(PeachLibrary STATIC pe_exceptions/pe_exception.cpp) +add_subdirectory(exceptions) -set_target_properties(PeachLibrary PROPERTIES LINKER_LANGUAGE CXX) +include(CMakePackageConfigHelpers) + +# generate the version file for the config file +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion +) + +# create config file +configure_package_config_file( + config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake + INSTALL_DESTINATION lib/cmake/${PROJECT_NAME} + NO_CHECK_REQUIRED_COMPONENTS_MACRO +) + +# generate the export targets for the build tree. +# Note: The file created by this command is specific to the build +# tree and should never be installed! +export( + EXPORT ${PROJECT_NAME}-targets + FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-targets.cmake + NAMESPACE ${PROJECT_NAME}:: +) + +# install cmake project config files +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake + DESTINATION lib/cmake/${PROJECT_NAME} +) diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 0000000..8b81abe --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,15 @@ +{ + "configurations": [ + { + "name": "x64-Clang-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": [ "msvc_x64_x64" ] + } + ] +} \ No newline at end of file diff --git a/config.cmake.in b/config.cmake.in new file mode 100644 index 0000000..b38170a --- /dev/null +++ b/config.cmake.in @@ -0,0 +1,15 @@ +@PACKAGE_INIT@ + +if(@COMPONENT_TARGETS_ENABLED@) + set(_supported_components exceptions) + + foreach(_comp ${peach_library_FIND_COMPONENTS}) + if(NOT _comp IN_LIST _supported_components) + set(peach_library_FOUND False) + set(peach_library_NOT_FOUND_MESSAGE "Unsupported component: ${_comp}") + endif() + include("${CMAKE_CURRENT_LIST_DIR}/peach_library-${_comp}-targets.cmake") + endforeach() +else() + include("${CMAKE_CURRENT_LIST_DIR}/peach_library-targets.cmake") +endif() \ No newline at end of file diff --git a/exceptions/CMakeLists.txt b/exceptions/CMakeLists.txt new file mode 100644 index 0000000..b7feb0a --- /dev/null +++ b/exceptions/CMakeLists.txt @@ -0,0 +1,36 @@ +set(component exceptions) + +add_library(${component} STATIC src/exception.cpp) + +target_compile_features(${component} PRIVATE cxx_std_20) + +target_include_directories(${component} PUBLIC $ + $ +) + +install( + TARGETS ${component} + EXPORT ${PROJECT_NAME}-targets + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + RUNTIME DESTINATION bin + INCLUDES DESTINATION include +) + +if(COMPONENT_TARGETS_ENABLED) + # generate and install export file for this component target + install( + EXPORT ${PROJECT_NAME}-targets + FILE ${PROJECT_NAME}-${component}-targets.cmake + NAMESPACE ${PROJECT_NAME}:: + DESTINATION lib/cmake/${PROJECT_NAME} + ) +endif() + +# install header files +install( + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + TYPE INCLUDE + FILES_MATCHING + PATTERN "*.hpp" +) diff --git a/exceptions/include/peach_library/exceptions/core/base_exception.hpp b/exceptions/include/peach_library/exceptions/core/base_exception.hpp new file mode 100644 index 0000000..81ecd82 --- /dev/null +++ b/exceptions/include/peach_library/exceptions/core/base_exception.hpp @@ -0,0 +1,43 @@ +#ifndef PEACH_BASE_EXCEPTION_HPP +#define PEACH_BASE_EXCEPTION_HPP + +#include +#include +#include +#include +#include +#include + +namespace peach::detail +{ + class BaseException : virtual public std::runtime_error + { + public: + BaseException( ) = default; + + // clang-format off + BaseException( const BaseException& ) noexcept = default; + BaseException(BaseException&& ) noexcept = default; + + BaseException& operator=(BaseException&& ) = default; + BaseException& operator=( const BaseException& ) = delete; + // clang-format on + + virtual const std::string& what_str( ) const noexcept = 0; + virtual void print_to_console( ) const noexcept = 0; + + virtual ~BaseException( ) { }; + }; + + template< typename T > + std::ostream& operator<<( std::ostream& output_file, + const T& rhs ) requires( std::derived_from< T, BaseException > ) + { + output_file << rhs.what_str( ); + + return output_file; + } + +} // namespace peach::detail + +#endif // PEACH_BASE_EXCEPTION_HPP diff --git a/exceptions/include/peach_library/exceptions/core/prefix_manager.hpp b/exceptions/include/peach_library/exceptions/core/prefix_manager.hpp new file mode 100644 index 0000000..3bb1cb4 --- /dev/null +++ b/exceptions/include/peach_library/exceptions/core/prefix_manager.hpp @@ -0,0 +1,82 @@ +#ifndef PEACH_PREFIX_MANAGER_HPP +#define PEACH_PREFIX_MANAGER_HPP + +#include + +namespace peach +{ + enum class ExceptionTypes; +} // namespace peach + +namespace peach +{ + + // Short info: + // * Provides general way to set default prefix + // Link docu: + class PrefixManager + { + public: + PrefixManager( ) noexcept = default; + + PrefixManager( const PrefixManager& ) noexcept = delete; + PrefixManager( PrefixManager&& ) noexcept = delete; + + PrefixManager& operator=( PrefixManager&& ) = delete; + PrefixManager& operator=( const PrefixManager& ) = delete; + + // clang-format off + static void set_default_prefix( const std::string& prefix ) noexcept + { + m_default_prefix = prefix; + } + // clang-format on + + inline static std::string m_default_prefix = { "LOG" }; + + ~PrefixManager( ) noexcept = default; + }; + + // Short info: + // * Defines and manages a prefix for each specific ExceptionType + // Link docu: + template< ExceptionTypes V > class Prefix : private PrefixManager + { + public: + static Prefix& get( ) noexcept + { + static Prefix< V > instance { }; + return instance; + } + + Prefix( ) noexcept = default; + + Prefix( const Prefix& ) noexcept = delete; + Prefix( Prefix&& ) noexcept = delete; + + Prefix& operator=( Prefix&& ) = delete; + Prefix& operator=( const Prefix& ) = delete; + + // clang-format off + void set_prefix( const std::string& prefix ) noexcept + { + m_prefix = prefix; + } + + std::string get_prefix( ) const noexcept + { + if ( m_prefix.empty( ) ) + return m_default_prefix; + + return m_prefix; + } + // clang-format on + + std::string m_prefix; + + ~Prefix( ) noexcept = default; + }; + +} // namespace peach + +#endif // PEACH_PREFIX_MANAGER_HPP diff --git a/exceptions/include/peach_library/exceptions/exception.hpp b/exceptions/include/peach_library/exceptions/exception.hpp new file mode 100644 index 0000000..1963b76 --- /dev/null +++ b/exceptions/include/peach_library/exceptions/exception.hpp @@ -0,0 +1,84 @@ +#ifndef PEACH_EXCEPTION_HPP +#define PEACH_EXCEPTION_HPP + +#include "core/base_exception.hpp" +#include "core/prefix_manager.hpp" + +#include "utils/exception_constants.hpp" +#include "utils/exception_format.hpp" + +#include +#include +#include +#include +#include +#include + +namespace peach +{ + // Short info: + // * This has to be explicitly defined by the user inside the peach namespace + // Link docu: + enum class ExceptionTypes; + + // Short info: + // * The method what() isnt templated in the base class, so we cannot override what() + // * First ctor handles the case when std::source_location information is wanted, second does it without + // Link docu: + template< ExceptionTypes Val > class PeachException : virtual public detail::BaseException + { + public: + template< typename... Tys > + explicit PeachException( const std::source_location sl, Tys&&... args ) + : std::runtime_error { detail::format_error( sl.line( ), sl.file_name( ), + Prefix< Val >::get( ).get_prefix( ), + std::forward< Tys >( args )... ) } + + { + m_err_msg = this->what( ); + m_prefix = Prefix< Val >::get( ).get_prefix( ); + } + + template< typename... Tys > + explicit PeachException( Tys&&... args ) + : std::runtime_error { detail::format_error( detail::constants::kNoSourceLocation, "", + Prefix< Val >::get( ).get_prefix( ), + std::forward< Tys >( args )... ) } + { + m_err_msg = this->what( ); + m_prefix = Prefix< Val >::get( ).get_prefix( ); + } + + PeachException( const PeachException& ) noexcept = default; + PeachException( PeachException&& ) noexcept = default; + + PeachException& operator=( PeachException&& ) = delete; + PeachException& operator=( const PeachException& ) = delete; + + void print_to_console( ) const noexcept + { + // TODO: possibly determine if compiled application has a console available! + std::cerr << m_prefix << R"(( " )" << m_err_msg.substr( 0, m_err_msg.size( ) - 2 ) + << R"( " ))" << '\n'; + } + + const std::string& what_str( ) const noexcept { return m_err_msg; } + + ~PeachException( ) = default; + + private: + std::string m_err_msg; + std::string m_prefix; + }; + + namespace detail + { + std::source_location + get_source_location_details( const std::source_location sl = std::source_location::current()) noexcept; + } // namespace detail + +#define SL detail::get_source_location_details( ) + +} // namespace peach + +#endif // PEACH_EXCEPTION_HPP diff --git a/exceptions/include/peach_library/exceptions/utils/exception_constants.hpp b/exceptions/include/peach_library/exceptions/utils/exception_constants.hpp new file mode 100644 index 0000000..2d79f0f --- /dev/null +++ b/exceptions/include/peach_library/exceptions/utils/exception_constants.hpp @@ -0,0 +1,10 @@ +#ifndef PEACH_EXCEPTION_CONSTANTS_HPP +#define PEACH_EXCEPTION_CONSTANTS_HPP + +namespace peach::detail::constants +{ + constexpr int kNoSourceLocation { -1 }; + +} // namespace peach::detail::constants + +#endif // PEACH_EXCEPTION_CONSTANTS_HPP diff --git a/exceptions/include/peach_library/exceptions/utils/exception_format.hpp b/exceptions/include/peach_library/exceptions/utils/exception_format.hpp new file mode 100644 index 0000000..abefa41 --- /dev/null +++ b/exceptions/include/peach_library/exceptions/utils/exception_format.hpp @@ -0,0 +1,44 @@ +#ifndef PEACH_EXCEPTION_FORMAT_HPP +#define PEACH_EXCEPTION_FORMAT_HPP + +#include +#include +#include +#include +#include + +#include "exception_safety.hpp" + +namespace peach::detail +{ + template< OutputStreamInsertable... Tys > + std::string format_error( const int line_num, const std::string& file_name, + const std::string& prefix, Tys&&... args ) + { + std::stringstream ss { }; + + const time_t t { std::time( nullptr ) }; + tm current_time { }; + localtime_s( ¤t_time, &t ); + + const auto put_time = std::put_time( ¤t_time, "[%T]" ); + + if ( line_num == constants::kNoSourceLocation && file_name.empty( ) ) + { + ss << '[' << prefix << ']' << put_time << " -> { "; + ( ( ss << std::forward< Tys >( args ) << ' ' ), ... ); + ss << " }\n\n"; + } else + { + ss << '[' << prefix << ']' << put_time << " at (line:file) " << line_num << " : " << file_name + << " ->\n\t\t{ "; + ( ( ss << std::forward< Tys >( args ) << ' ' ), ... ); + ss << " }\n\n"; + } + + return std::string { ss.str( ) }; + } + +} // namespace peach::detail + +#endif // PEACH_EXCEPTION_FORMAT_HPP diff --git a/exceptions/include/peach_library/exceptions/utils/exception_safety.hpp b/exceptions/include/peach_library/exceptions/utils/exception_safety.hpp new file mode 100644 index 0000000..ea89c1b --- /dev/null +++ b/exceptions/include/peach_library/exceptions/utils/exception_safety.hpp @@ -0,0 +1,33 @@ +#ifndef PEACH_EXCEPTION_SAFETY_HPP +#define PEACH_EXCEPTION_SAFETY_HPP + +#include +#include +#include + +namespace peach::detail +{ + + // prevents possibility of a this pointer being passed in the format_error context + /* TODO : Figure out if this is still needed + + template< typename T > + inline std::ostream& + operator<<( std::ostream& stream, + [[maybe_unused]] const T& ec ) requires( std::derived_from< T, pe_base_exception > ) + { + return stream; + } + + */ + + // clang-format off + template< typename T > concept OutputStreamInsertable = requires( std::ostream& os, const T& arg ) + { + { os << arg } ->std::convertible_to< std::ostream& >; + }; + // clang-format on + +} // namespace peach::detail + +#endif // PEACH_EXCEPTION_SAFETY_HPP diff --git a/exceptions/src/exception.cpp b/exceptions/src/exception.cpp new file mode 100644 index 0000000..67de2df --- /dev/null +++ b/exceptions/src/exception.cpp @@ -0,0 +1,11 @@ +#include + +#include + +namespace peach::detail +{ + std::source_location get_source_location_details( const std::source_location sl ) noexcept + { + return sl; + } +} // namespace peach::detail diff --git a/pe_exceptions/pe_exception.cpp b/pe_exceptions/pe_exception.cpp deleted file mode 100644 index bee9765..0000000 --- a/pe_exceptions/pe_exception.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "pe_exception.hpp" - -using namespace peach; diff --git a/pe_exceptions/pe_exception.hpp b/pe_exceptions/pe_exception.hpp deleted file mode 100644 index 859cd0c..0000000 --- a/pe_exceptions/pe_exception.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef PE_EXCEPTION_HPP -#define PE_EXCEPTION_HPP - -#include -#include -#include -#include -#include - -namespace peach -{ - class pe_fatal_error; - inline std::ostream& operator<<( std::ostream& stream, [[maybe_unused]] const pe_fatal_error& ec ); - - template< typename T > concept OutputStreamInsertable = requires( std::ostream& os, const T& arg ) - { - { - os << arg - } - ->std::convertible_to< std::ostream& >; - }; - - template< OutputStreamInsertable... Tys > - std::string format_error( const size_t line_num, const std::string& file_name, Tys&&... args ) - { - std::stringstream ss { }; - ( ( ss << std::forward< Tys >( args ) << " " ), ... ); - return ss.str( ); - } - - class pe_fatal_error : public std::runtime_error - { - public: - template< typename... Tys > - explicit pe_fatal_error( const size_t line_num, const std::string& file_name, Tys&&... args ) - : std::runtime_error { format_error( line_num, file_name, std::forward< Tys >( args )... ) } - { - } - - pe_fatal_error( const pe_fatal_error& ) noexcept = default; - - pe_fatal_error( pe_fatal_error&& ) noexcept = delete; - - pe_fatal_error& operator=( pe_fatal_error&& ) = delete; - - pe_fatal_error& operator=( const pe_fatal_error& ) = delete; - - ~pe_fatal_error( ) = default; - }; - - inline std::ostream& operator<<( std::ostream& stream, [[maybe_unused]] const pe_fatal_error& ec ) { return stream; } - - template< typename... Tys > - pe_fatal_error throw_exception( const size_t line_num, const std::string file_name, Tys&&... err_message ) - { - return pe_fatal_error( line_num, file_name, std::forward< Tys >( err_message )... ); - } - -#define Exception( ... ) throw_exception( __LINE__, __FILE__, ##__VA_ARGS__ ) - -} // namespace peach - -#endif //PE_EXCEPTION_HPP diff --git a/peach_library-config.cmake b/peach_library-config.cmake new file mode 100644 index 0000000..c1694e8 --- /dev/null +++ b/peach_library-config.cmake @@ -0,0 +1,3 @@ +foreach(component ${peach_library_FIND_COMPONENTS}) + include(${CMAKE_CURRENT_LIST_DIR}/peach_library-${component}-config.cmake) +endforeach()