From 479b1879bb66ff7611a3d6979d6b21f694f3dd6b Mon Sep 17 00:00:00 2001 From: Bart Beumer Date: Thu, 1 Dec 2022 22:18:21 +0100 Subject: [PATCH] [#1] First version of bmrshared. --- .clang-format | 37 ++++ .clang-tidy | 38 ++++ 3rdparty-boost.cmake | 25 +++ 3rdparty-googletest.cmake | 37 ++++ CMakeLists.txt | 8 + LICENSE.txt | 165 ++++++++++++++++++ README.md | 3 - bmrshared/CMakeLists.txt | 2 + bmrshared/include/bmrshared/IDataSource.hpp | 27 +++ .../include/bmrshared/ToTupleException.hpp | 45 +++++ .../include/bmrshared/flexible_value.hpp | 72 ++++++++ bmrshared/include/bmrshared/function_info.hpp | 33 ++++ .../include/bmrshared/function_wrapper.hpp | 58 ++++++ bmrshared/include/bmrshared/invoke.hpp | 103 +++++++++++ bmrshared/include/bmrshared/to_tuple.hpp | 81 +++++++++ bmrshared/lib/CMakeLists.txt | 20 +++ bmrshared/lib/ToTupleException.cpp | 64 +++++++ bmrshared/lib/flexible_value.cpp | 43 +++++ bmrshared/lib/to_tuple.cpp | 50 ++++++ bmrshared/tst/CMakeLists.txt | 28 +++ bmrshared/tst/InvokeTest.cpp | 125 +++++++++++++ bmrshared/tst/MockDataSource.hpp | 29 +++ bmrshared/tst/ToTupleTest.cpp | 85 +++++++++ bmrshared/tst/WrapTest.cpp | 79 +++++++++ empty.cpp | 0 25 files changed, 1254 insertions(+), 3 deletions(-) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 3rdparty-boost.cmake create mode 100644 3rdparty-googletest.cmake create mode 100644 CMakeLists.txt create mode 100644 LICENSE.txt delete mode 100644 README.md create mode 100644 bmrshared/CMakeLists.txt create mode 100644 bmrshared/include/bmrshared/IDataSource.hpp create mode 100644 bmrshared/include/bmrshared/ToTupleException.hpp create mode 100644 bmrshared/include/bmrshared/flexible_value.hpp create mode 100644 bmrshared/include/bmrshared/function_info.hpp create mode 100644 bmrshared/include/bmrshared/function_wrapper.hpp create mode 100644 bmrshared/include/bmrshared/invoke.hpp create mode 100644 bmrshared/include/bmrshared/to_tuple.hpp create mode 100644 bmrshared/lib/CMakeLists.txt create mode 100644 bmrshared/lib/ToTupleException.cpp create mode 100644 bmrshared/lib/flexible_value.cpp create mode 100644 bmrshared/lib/to_tuple.cpp create mode 100644 bmrshared/tst/CMakeLists.txt create mode 100644 bmrshared/tst/InvokeTest.cpp create mode 100644 bmrshared/tst/MockDataSource.hpp create mode 100644 bmrshared/tst/ToTupleTest.cpp create mode 100644 bmrshared/tst/WrapTest.cpp create mode 100644 empty.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..9fc6dd5 --- /dev/null +++ b/.clang-format @@ -0,0 +1,37 @@ +--- +AlignAfterOpenBracket: Align +AllowAllArgumentsOnNextLine: 'false' +AllowAllConstructorInitializersOnNextLine: 'false' +AllowAllParametersOfDeclarationOnNextLine: 'false' +AllowShortBlocksOnASingleLine: 'false' +AllowShortCaseLabelsOnASingleLine: 'false' +AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: None +AllowShortLoopsOnASingleLine: 'false' +AlwaysBreakTemplateDeclarations: 'Yes' +BinPackArguments: 'false' +BinPackParameters: 'false' +BreakBeforeBraces: Allman +BreakConstructorInitializers: BeforeComma +ColumnLimit: '120' +Cpp11BracedListStyle: 'true' +FixNamespaceComments: 'true' +IncludeBlocks: Merge +IndentCaseLabels: 'true' +IndentWidth: '4' +Language: Cpp +MaxEmptyLinesToKeep: '2' +NamespaceIndentation: Inner +PointerAlignment: Left +SortUsingDeclarations: 'true' +SpaceAfterTemplateKeyword: 'false' +SpacesInAngles: 'false' +SpacesInCStyleCastParentheses: 'false' +SpacesInContainerLiterals: 'false' +SpacesInSquareBrackets: 'false' +Standard: Cpp11 +TabWidth: '4' +UseTab: Never + +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..480cc03 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,38 @@ +--- +Checks: 'clang-diagnostic-*,clang-analyzer-*,cppcoreguidelines-*,modernize-*,-modernize-use-trailing-return-type' +WarningsAsErrors: true +HeaderFileExtensions: ['', 'h','hh','hpp','hxx'] +ImplementationFileExtensions: ['c','cc','cpp','cxx'] +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false +FormatStyle: google +CheckOptions: + - key: cert-dcl16-c.NewSuffixes + value: 'L;LL;LU;LLU' + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: '0' + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: '1' + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: '1' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-use-nullptr.NullMacros + value: 'NULL' +... diff --git a/3rdparty-boost.cmake b/3rdparty-boost.cmake new file mode 100644 index 0000000..b907e8b --- /dev/null +++ b/3rdparty-boost.cmake @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.0) + +project(3rdparty-boost) +add_library( + ${PROJECT_NAME} + STATIC + empty.cpp +) + +set_property( + TARGET ${PROJECT_NAME} + PROPERTY CXX_STANDARD 20 +) + +target_link_libraries( + ${PROJECT_NAME} + INTERFACE + /home/bart/Development/3rd-party/boost/boost_1_81_0/lib/libboost_url.a + /home/bart/Development/3rd-party/boost/boost_1_81_0/lib/libboost_json.a +) + +target_include_directories( + ${PROJECT_NAME} + PUBLIC /home/bart/Development/3rd-party/boost/boost_1_81_0/include/ +) diff --git a/3rdparty-googletest.cmake b/3rdparty-googletest.cmake new file mode 100644 index 0000000..c73b77e --- /dev/null +++ b/3rdparty-googletest.cmake @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.0) + +project(3rdparty-googletest) + +find_package(GTest REQUIRED) + +add_library( + ${PROJECT_NAME} + STATIC + empty.cpp +) + +target_link_libraries( + ${PROJECT_NAME} + INTERFACE + GTest::gtest + GTest::gmock +) + + + +project(3rdparty-googletest-main) + +add_library( + ${PROJECT_NAME} + STATIC + empty.cpp +) + +target_link_libraries( + ${PROJECT_NAME} + INTERFACE + 3rdparty-googletest + GTest::gtest_main +) + + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3e95320 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.0) + +project(all) +enable_testing() + +include(3rdparty-boost.cmake) +include(3rdparty-googletest.cmake) +add_subdirectory(bmrshared) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..8805d0d --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,165 @@ +GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md deleted file mode 100644 index c8d1406..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# network-experiment - -Networking using C++ and boost beast. \ No newline at end of file diff --git a/bmrshared/CMakeLists.txt b/bmrshared/CMakeLists.txt new file mode 100644 index 0000000..86d49d9 --- /dev/null +++ b/bmrshared/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(lib) +add_subdirectory(tst) diff --git a/bmrshared/include/bmrshared/IDataSource.hpp b/bmrshared/include/bmrshared/IDataSource.hpp new file mode 100644 index 0000000..7269900 --- /dev/null +++ b/bmrshared/include/bmrshared/IDataSource.hpp @@ -0,0 +1,27 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#pragma once +#include +#include +#include +#include + +namespace bmrshared +{ +class IDataSource +{ + public: + virtual ~IDataSource() = default; + virtual void Get(std::string_view id, bool& out) const = 0; + virtual void Get(std::string_view id, double& out) const = 0; + virtual void Get(std::string_view id, int64_t& out) const = 0; + virtual void Get(std::string_view id, uint64_t& out) const = 0; + virtual void Get(std::string_view id, std::string& out) const = 0; + virtual std::vector GetKeys() const = 0; +}; +} // namespace bmrshared diff --git a/bmrshared/include/bmrshared/ToTupleException.hpp b/bmrshared/include/bmrshared/ToTupleException.hpp new file mode 100644 index 0000000..f3b021b --- /dev/null +++ b/bmrshared/include/bmrshared/ToTupleException.hpp @@ -0,0 +1,45 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#pragma once +#include +#include +#include + +namespace bmrshared +{ +class ToTupleException : public std::exception +{ + public: + struct ParamError + { + std::string name; + std::string type; + }; + ToTupleException(std::vector keysMissing, + std::vector keysNotUsed, + std::vector paramErrors); + + ~ToTupleException() override; + + const std::vector& GetKeysMissing() const; + const std::vector& GetKeysNotUsed() const; + + const char* what() const noexcept override; + + private: + static std::string GenerateWhat(const std::vector& keysMissing, + const std::vector& keysNotUsed, + const std::vector& paramErrors); + + private: + std::vector m_keysMissing; + std::vector m_keysNotUsed; + std::vector m_paramErrors; + std::string m_what; +}; +} // namespace bmrshared diff --git a/bmrshared/include/bmrshared/flexible_value.hpp b/bmrshared/include/bmrshared/flexible_value.hpp new file mode 100644 index 0000000..e743095 --- /dev/null +++ b/bmrshared/include/bmrshared/flexible_value.hpp @@ -0,0 +1,72 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#pragma once +#include +#include +#include +#include +#include + +namespace bmrshared +{ +class flexible_value final +{ + public: + using keyvalue_type = std::map; + using variant_type = std::variant, + std::vector, + std::vector, + std::vector, + std::vector, + keyvalue_type>; + + flexible_value(); + flexible_value(const variant_type& value); + ~flexible_value(); + + flexible_value& operator=(const flexible_value& other); + flexible_value& operator=(const variant_type& other); + + template + const T& as() const + { + return std::get(m_data); + } + + template + T& as() + { + return std::get(m_data); + } + + template + const std::vector& as_array() const + { + return as>(); + } + + template + std::vector& as_array() + { + return as>(); + } + + const keyvalue_type& as_keyvalue() const; + keyvalue_type& as_keyvalue(); + const variant_type& as_variant() const; + + private: + variant_type m_data; +}; + +} // namespace bmrshared diff --git a/bmrshared/include/bmrshared/function_info.hpp b/bmrshared/include/bmrshared/function_info.hpp new file mode 100644 index 0000000..fff2650 --- /dev/null +++ b/bmrshared/include/bmrshared/function_info.hpp @@ -0,0 +1,33 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#pragma once +#include +#include + +namespace bmrshared +{ +template +struct function_info; + +template +struct function_info +{ + using result_type = R; + using argument_types = std::tuple; + static constexpr bool is_const_member = true; +}; + +template +struct function_info +{ + using result_type = R; + using argument_types = std::tuple; + static constexpr bool is_const_member = false; +}; + +} // namespace bmrshared diff --git a/bmrshared/include/bmrshared/function_wrapper.hpp b/bmrshared/include/bmrshared/function_wrapper.hpp new file mode 100644 index 0000000..8ee940f --- /dev/null +++ b/bmrshared/include/bmrshared/function_wrapper.hpp @@ -0,0 +1,58 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#pragma once +#include "flexible_value.hpp" +#include "invoke.hpp" +#include +#include +#include +#include + +namespace bmrshared +{ +class IDataSource; + + +template +struct function_wrapper; + + +template +requires(std::is_const::value == true) struct function_wrapper +{ + using wrapper_type = std::function; + + template + static wrapper_type Wrap(M T::*callableFn, TSourceParams... paramNames) + { + std::array names{paramNames...}; + return [callableFn, names](const T& obj, const IDataSource& source, TExtraArguments... args) + { + return invoke(obj, callableFn, names, source, args...); + }; + } +}; + + +template +requires(std::is_const::value == false) struct function_wrapper +{ + using wrapper_type = std::function; + + template + static wrapper_type Wrap(M T::*callableFn, TSourceParams... paramNames) + { + std::array names{paramNames...}; + return [callableFn, names](T& obj, const IDataSource& source, TExtraArguments... args) + { + return invoke(obj, callableFn, names, source, args...); + }; + } +}; + +} // namespace bmrshared diff --git a/bmrshared/include/bmrshared/invoke.hpp b/bmrshared/include/bmrshared/invoke.hpp new file mode 100644 index 0000000..9f6913f --- /dev/null +++ b/bmrshared/include/bmrshared/invoke.hpp @@ -0,0 +1,103 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#pragma once +#include "IDataSource.hpp" +#include "flexible_value.hpp" +#include "function_info.hpp" +#include "to_tuple.hpp" +#include +#include +#include +#include + +namespace bmrshared +{ +namespace detail +{ + template + struct tuple_keepelemsfront; + + template + struct tuple_keepelemsfront, 0, TypesToKeep...> + { + using tuple = std::tuple; + }; + + template + requires(NTypesToKeep > 0) + + struct tuple_keepelemsfront, NTypesToKeep, TypesToKeep...> + { + using tuple = typename tuple_keepelemsfront, + (NTypesToKeep - 1), + TypesToKeep..., + FirstTupleType>::tuple; + }; + + + template + struct tuple_rmelemsback; + + template + struct tuple_rmelemsback, NTypesToRemove> + { + using tuple = + typename tuple_keepelemsfront, (sizeof...(TupleTypes) - NTypesToRemove)>::tuple; + }; +} // namespace detail + +template +requires(function_info::is_const_member == false) + + flexible_value invoke(T& callableObj, + M T::*callableFn, + std::array names, + const IDataSource& source, + TExtraArguments... arguments) +{ + using CallableInfo = function_info; + using SourceArgumentsTuple = + typename detail::tuple_rmelemsback::tuple; + + SourceArgumentsTuple parameters; + to_tuple(source, names, parameters); + + return {std::apply(std::mem_fn(callableFn), + std::tuple_cat(std::tie(callableObj), parameters, std::tie(arguments...)))}; +} + +template +requires(function_info::is_const_member == true) + + flexible_value invoke(const T& callableObj, + M T::*callableFn, + std::array names, + const IDataSource& source, + TExtraArguments... arguments) +{ + using CallableInfo = function_info; + using SourceArgumentsTuple = + typename detail::tuple_rmelemsback::tuple; + + SourceArgumentsTuple parameters; + to_tuple(source, names, parameters); + + return {std::apply(std::mem_fn(callableFn), + std::tuple_cat(std::tie(callableObj), parameters, std::tie(arguments...)))}; +} + +template +flexible_value invoke(TCallable& fn, + std::array names, + const IDataSource& source, + TExtraArguments... arguments) +{ + return invoke(fn, &TCallable::operator(), names, source, arguments...); +} + +} // namespace bmrshared diff --git a/bmrshared/include/bmrshared/to_tuple.hpp b/bmrshared/include/bmrshared/to_tuple.hpp new file mode 100644 index 0000000..30f0b53 --- /dev/null +++ b/bmrshared/include/bmrshared/to_tuple.hpp @@ -0,0 +1,81 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#pragma once +#include "IDataSource.hpp" +#include "ToTupleException.hpp" +#include +#include +#include +#include +#include +#include + +namespace bmrshared +{ +namespace detail +{ + std::vector + GetKeysMissing(const std::vector& allKeys, const std::string* dataBegin, std::size_t arraySize); + std::vector + GetKeysNotUsed(const std::vector& allKeys, const std::string* dataBegin, std::size_t arraySize); + + + template + T GetValueFromDataSource(const IDataSource& source, + std::string_view id, + std::vector& outErrors) + { + T value; + try + { + source.Get(id, value); + } + catch (const std::exception&) + { + outErrors.push_back(ToTupleException::ParamError{std::string(id), "problem reading"}); + } + return value; + } + + template + std::tuple to_tuple(const IDataSource& source, + const std::array& names, + std::index_sequence) + { + const auto allKeys = source.GetKeys(); + const auto keysMissing = GetKeysMissing(allKeys, names.data(), sizeof...(TParameters)); + const auto keysNotUsed = GetKeysNotUsed(allKeys, names.data(), sizeof...(TParameters)); + + // Constructing the tuple by expanding the parameter packs to retrieve each name for the specified type. in + // case of errors these are written to errorRecords + std::vector errorRecords; + std::tuple returnValue = { + GetValueFromDataSource(source, std::get(names), errorRecords)...}; + + if (!keysMissing.empty() || !keysNotUsed.empty() || !errorRecords.empty()) + { + throw ToTupleException(keysMissing, keysNotUsed, errorRecords); + } + return returnValue; + } +} // namespace detail + +template +std::tuple to_tuple(const IDataSource& source, const std::array& names) +{ + return detail::to_tuple(source, names, std::make_index_sequence()); +} + +template +void to_tuple(const IDataSource& source, + const std::array& names, + std::tuple& outParameters) +{ + outParameters = detail::to_tuple(source, names, std::make_index_sequence()); +} +} // namespace bmrshared diff --git a/bmrshared/lib/CMakeLists.txt b/bmrshared/lib/CMakeLists.txt new file mode 100644 index 0000000..2d1565d --- /dev/null +++ b/bmrshared/lib/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.0) + +project(bmrshared) + +add_library( + ${PROJECT_NAME} + flexible_value.cpp + to_tuple.cpp + ToTupleException.cpp +) + +set_property( + TARGET ${PROJECT_NAME} + PROPERTY CXX_STANDARD 20 +) + +target_include_directories( + ${PROJECT_NAME} + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include +) diff --git a/bmrshared/lib/ToTupleException.cpp b/bmrshared/lib/ToTupleException.cpp new file mode 100644 index 0000000..a1ebe46 --- /dev/null +++ b/bmrshared/lib/ToTupleException.cpp @@ -0,0 +1,64 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#include "bmrshared/ToTupleException.hpp" +#include +#include +#include +#include +#include + +namespace bmrshared +{ +ToTupleException::ToTupleException(std::vector keysMissing, + std::vector keysNotUsed, + std::vector paramErrors) + : m_keysMissing(std::move(keysMissing)) + , m_keysNotUsed(std::move(keysNotUsed)) + , m_paramErrors(std::move(paramErrors)) + , m_what(GenerateWhat(m_keysMissing, m_keysNotUsed, m_paramErrors)) +{ +} + +ToTupleException::~ToTupleException() = default; + +const std::vector& ToTupleException::GetKeysMissing() const +{ + return m_keysMissing; +} + +const std::vector& ToTupleException::GetKeysNotUsed() const +{ + return m_keysNotUsed; +} + +const char* ToTupleException::what() const noexcept +{ + return m_what.c_str(); +} + +std::string ToTupleException::GenerateWhat(const std::vector& keysMissing, + const std::vector& keysNotUsed, + const std::vector& paramErrors) +{ + std::stringstream ss; + ss << "Errors while converting to tuple."; + for (const auto& key : keysMissing) + { + ss << "\n - missing: " << key; + } + for (const auto& key : keysNotUsed) + { + ss << "\n - unused: " << key; + } + for (const auto& error : paramErrors) + { + ss << "\n - error: " << error.name << " \"" << error.type << "\""; + } + return ss.str(); +} +} // namespace bmrshared diff --git a/bmrshared/lib/flexible_value.cpp b/bmrshared/lib/flexible_value.cpp new file mode 100644 index 0000000..0bea4f1 --- /dev/null +++ b/bmrshared/lib/flexible_value.cpp @@ -0,0 +1,43 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#include "bmrshared/flexible_value.hpp" + +using bmrshared::flexible_value; + +flexible_value::flexible_value() = default; + +flexible_value::flexible_value(const variant_type& value) + : m_data(value) +{ +} + +flexible_value::~flexible_value() = default; + +flexible_value& flexible_value::operator=(const flexible_value& other) = default; + +flexible_value& flexible_value::operator=(const flexible_value::variant_type& other) +{ + m_data = other; + return *this; +} + + +const flexible_value::keyvalue_type& flexible_value::as_keyvalue() const +{ + return as(); +} + +flexible_value::keyvalue_type& flexible_value::as_keyvalue() +{ + return as(); +} + +const flexible_value::variant_type& flexible_value::as_variant() const +{ + return m_data; +} diff --git a/bmrshared/lib/to_tuple.cpp b/bmrshared/lib/to_tuple.cpp new file mode 100644 index 0000000..02b72de --- /dev/null +++ b/bmrshared/lib/to_tuple.cpp @@ -0,0 +1,50 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#include "bmrshared/to_tuple.hpp" +#include +#include +#include + +namespace bmrshared +{ +std::vector detail::GetKeysMissing(const std::vector& allKeys, + const std::string* const dataBegin, + std::size_t arraySize) +{ + const std::string* const dataEnd = (dataBegin + arraySize); + std::vector keysMissing; + + for (const std::string* iter = dataBegin; iter != dataEnd; ++iter) + { + auto iterFound = std::find(allKeys.begin(), allKeys.end(), *iter); + if (iterFound == allKeys.end()) + { + keysMissing.push_back(*iter); + } + } + return keysMissing; +} + +std::vector detail::GetKeysNotUsed(const std::vector& allKeys, + const std::string* const dataBegin, + std::size_t arraySize) +{ + const std::string* const dataEnd = (dataBegin + arraySize); + std::vector keysNotUsed; + + for (const auto& key : allKeys) + { + auto iterFound = std::find(dataBegin, dataEnd, key); + if (iterFound == dataEnd) + { + keysNotUsed.push_back(key); + } + } + return keysNotUsed; +} +} // namespace bmrshared diff --git a/bmrshared/tst/CMakeLists.txt b/bmrshared/tst/CMakeLists.txt new file mode 100644 index 0000000..81c05f8 --- /dev/null +++ b/bmrshared/tst/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.0) + +project(bmrshared-test) +add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME}) + +add_executable( + ${PROJECT_NAME} + ToTupleTest.cpp + InvokeTest.cpp + WrapTest.cpp +) + +set_property( + TARGET ${PROJECT_NAME} + PROPERTY CXX_STANDARD 20 +) + +target_link_libraries( + ${PROJECT_NAME} + PUBLIC + bmrshared + 3rdparty-googletest-main +) + +install( + TARGETS ${PROJECT_NAME} + RUNTIME DESTINATION bin +) diff --git a/bmrshared/tst/InvokeTest.cpp b/bmrshared/tst/InvokeTest.cpp new file mode 100644 index 0000000..ed6c10d --- /dev/null +++ b/bmrshared/tst/InvokeTest.cpp @@ -0,0 +1,125 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#include "bmrshared/invoke.hpp" +#include "MockDataSource.hpp" +#include +#include +#include + +using ::testing::An; +using ::testing::ElementsAre; +using ::testing::Return; +using ::testing::SetArgReferee; +using ::testing::TypedEq; + +using bmrshared::flexible_value; +using bmrshared::MockDataSource; + +namespace +{ +// Compile-time test of some helper templates. + +static_assert(std::is_same, 0>::tuple, + std::tuple<>>::value); +static_assert(std::is_same, 1>::tuple, + std::tuple>::value); +static_assert(std::is_same, 2>::tuple, + std::tuple>::value); + +static_assert(std::is_same, 0>::tuple, + std::tuple>::value); +static_assert(std::is_same, 1>::tuple, + std::tuple>::value); +static_assert( + std::is_same, 2>::tuple, std::tuple<>>::value); + + +class TestAddition +{ + public: + uint64_t AddNumbers(uint64_t left, uint64_t right) { return left + (right * 2); } + uint64_t AddNumbersConst(uint64_t left, uint64_t right) const { return left + (right * 2); } +}; +} // namespace + + +TEST(InvokeTest, Ok_Callable) +{ + auto fn = [](int64_t a, int64_t b) + { + return (a + (b * 2)); + }; + std::array names = {"a", "b"}; + + MockDataSource mockSrc; + EXPECT_CALL(mockSrc, GetKeys()).WillRepeatedly(Return(std::vector{"a", "b"})); + EXPECT_CALL(mockSrc, Get("a", An())).WillRepeatedly(SetArgReferee<1>(42)); + EXPECT_CALL(mockSrc, Get("b", An())).WillRepeatedly(SetArgReferee<1>(21)); + + flexible_value returnValue = bmrshared::invoke(fn, names, mockSrc); + EXPECT_EQ(returnValue.as(), 84); +} + +TEST(InvokeTest, Ok_Callable_LastArg) +{ + auto fn = [](int64_t a, int64_t b, int64_t lastArg) + { + return ((lastArg * 4) + a + (b * 2)); + }; + std::array names = {"a", "b"}; + + MockDataSource mockSrc; + EXPECT_CALL(mockSrc, GetKeys()).WillRepeatedly(Return(std::vector{"a", "b"})); + EXPECT_CALL(mockSrc, Get("a", An())).WillRepeatedly(SetArgReferee<1>(42)); + EXPECT_CALL(mockSrc, Get("b", An())).WillRepeatedly(SetArgReferee<1>(21)); + + flexible_value returnValue = bmrshared::invoke(fn, names, mockSrc, 1000); + EXPECT_EQ(returnValue.as(), 4084); +} + +TEST(InvokeTest, Ok_Ptr_Member) +{ + std::array names = {"left", "right"}; + + MockDataSource mockSrc; + EXPECT_CALL(mockSrc, GetKeys()).WillRepeatedly(Return(std::vector{"left", "right"})); + EXPECT_CALL(mockSrc, Get("left", An())).WillRepeatedly(SetArgReferee<1>(100)); + EXPECT_CALL(mockSrc, Get("right", An())).WillRepeatedly(SetArgReferee<1>(20)); + + TestAddition functor; + flexible_value returnValue = bmrshared::invoke(functor, &TestAddition::AddNumbers, names, mockSrc); + EXPECT_EQ(returnValue.as(), 140); +} + +TEST(InvokeTest, Ok_Ptr_Const_Member_Const) +{ + std::array names = {"left", "right"}; + + MockDataSource mockSrc; + EXPECT_CALL(mockSrc, GetKeys()).WillRepeatedly(Return(std::vector{"left", "right"})); + EXPECT_CALL(mockSrc, Get("left", An())).WillRepeatedly(SetArgReferee<1>(100)); + EXPECT_CALL(mockSrc, Get("right", An())).WillRepeatedly(SetArgReferee<1>(20)); + + const TestAddition functor; + flexible_value returnValue = bmrshared::invoke(functor, &TestAddition::AddNumbersConst, names, mockSrc); + EXPECT_EQ(returnValue.as(), 140); +} + +TEST(InvokeTest, Ok_Ptr_Member_Const) +{ + std::array names = {"left", "right"}; + + MockDataSource mockSrc; + EXPECT_CALL(mockSrc, GetKeys()).WillRepeatedly(Return(std::vector{"left", "right"})); + EXPECT_CALL(mockSrc, Get("left", An())).WillRepeatedly(SetArgReferee<1>(100)); + EXPECT_CALL(mockSrc, Get("right", An())).WillRepeatedly(SetArgReferee<1>(20)); + + TestAddition functor; + flexible_value returnValue = bmrshared::invoke(functor, &TestAddition::AddNumbersConst, names, mockSrc); + EXPECT_EQ(returnValue.as(), 140); +} diff --git a/bmrshared/tst/MockDataSource.hpp b/bmrshared/tst/MockDataSource.hpp new file mode 100644 index 0000000..21b00aa --- /dev/null +++ b/bmrshared/tst/MockDataSource.hpp @@ -0,0 +1,29 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#pragma once +#include "bmrshared/IDataSource.hpp" +#include +#include +#include +#include +#include + +namespace bmrshared +{ +class MockDataSource : public IDataSource +{ + public: + ~MockDataSource() override = default; + MOCK_METHOD(void, Get, (std::string_view, bool&), (const, override)); + MOCK_METHOD(void, Get, (std::string_view, double&), (const, override)); + MOCK_METHOD(void, Get, (std::string_view, int64_t&), (const, override)); + MOCK_METHOD(void, Get, (std::string_view, uint64_t&), (const, override)); + MOCK_METHOD(void, Get, (std::string_view, std::string&), (const, override)); + MOCK_METHOD(std::vector, GetKeys, (), (const, override)); +}; +} // namespace bmrshared diff --git a/bmrshared/tst/ToTupleTest.cpp b/bmrshared/tst/ToTupleTest.cpp new file mode 100644 index 0000000..34a2c67 --- /dev/null +++ b/bmrshared/tst/ToTupleTest.cpp @@ -0,0 +1,85 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#include "MockDataSource.hpp" +#include "bmrshared/IDataSource.hpp" +#include "bmrshared/flexible_value.hpp" +#include "bmrshared/to_tuple.hpp" +#include +#include +#include + +using ::bmrshared::MockDataSource; +using ::testing::An; +using ::testing::ElementsAre; +using ::testing::Return; +using ::testing::SetArgReferee; +using ::testing::Throw; + +TEST(ToTupleTest, Ok) +{ + MockDataSource mockSrc; + EXPECT_CALL(mockSrc, GetKeys()).WillRepeatedly(Return(std::vector{"a", "b", "c"})); + EXPECT_CALL(mockSrc, Get("a", An())).WillRepeatedly(SetArgReferee<1>(42)); + EXPECT_CALL(mockSrc, Get("b", An())).WillRepeatedly(SetArgReferee<1>(4.2)); + EXPECT_CALL(mockSrc, Get("c", An())).WillRepeatedly(SetArgReferee<1>("test")); + + using TupleType = std::tuple; + const std::array names{"a", "b", "c"}; + const TupleType expected{42, 4.2, "test"}; + + TupleType result; + bmrshared::to_tuple(mockSrc, names, result); + + EXPECT_EQ(expected, result); +} + +TEST(ToTupleTest, MissingParameter) +{ + MockDataSource mockSrc; + EXPECT_CALL(mockSrc, GetKeys()).WillRepeatedly(Return(std::vector{"a", "c"})); + EXPECT_CALL(mockSrc, Get("a", An())).WillRepeatedly(SetArgReferee<1>(42)); + EXPECT_CALL(mockSrc, Get("b", An())).WillRepeatedly(Throw(std::runtime_error(""))); + EXPECT_CALL(mockSrc, Get("c", An())).WillRepeatedly(SetArgReferee<1>("test")); + + using TupleType = std::tuple; + const std::array names{"a", "b", "c"}; + try + { + TupleType result; + bmrshared::to_tuple(mockSrc, names, result); + EXPECT_TRUE(false); + } + catch (const bmrshared::ToTupleException& e) + { + EXPECT_THAT(e.GetKeysMissing(), ElementsAre("b")); + EXPECT_THAT(e.GetKeysNotUsed(), ElementsAre()); + } +} + +TEST(ToTupleTest, NotUsedParameter) +{ + MockDataSource mockSrc; + EXPECT_CALL(mockSrc, GetKeys()).WillRepeatedly(Return(std::vector{"a", "b", "c", "d"})); + EXPECT_CALL(mockSrc, Get("a", An())).WillRepeatedly(SetArgReferee<1>(42)); + EXPECT_CALL(mockSrc, Get("b", An())).WillRepeatedly(SetArgReferee<1>(4.2)); + EXPECT_CALL(mockSrc, Get("c", An())).WillRepeatedly(SetArgReferee<1>("test")); + + using TupleType = std::tuple; + const std::array names{"a", "b", "c"}; + try + { + TupleType result; + bmrshared::to_tuple(mockSrc, names, result); + EXPECT_TRUE(false); + } + catch (const bmrshared::ToTupleException& e) + { + EXPECT_THAT(e.GetKeysMissing(), ElementsAre()); + EXPECT_THAT(e.GetKeysNotUsed(), ElementsAre("d")); + } +} diff --git a/bmrshared/tst/WrapTest.cpp b/bmrshared/tst/WrapTest.cpp new file mode 100644 index 0000000..fb0a79c --- /dev/null +++ b/bmrshared/tst/WrapTest.cpp @@ -0,0 +1,79 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2023 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#include "MockDataSource.hpp" +#include "bmrshared/function_wrapper.hpp" +#include +#include + +using ::testing::An; +using ::testing::ElementsAre; +using ::testing::Return; +using ::testing::SetArgReferee; +using ::testing::TypedEq; + +using ::bmrshared::flexible_value; +using ::bmrshared::function_wrapper; +using ::bmrshared::IDataSource; +using ::bmrshared::MockDataSource; + + +namespace +{ +class TestAddition +{ + public: + int64_t AddNumbers(int64_t left, int64_t right) { return left + (right * 2); } + int64_t AddNumbersConst(int64_t left, int64_t right) const { return left + (right * 2); } +}; + +using TestWrapperType = function_wrapper; +using TestConstWrapperType = function_wrapper; + +} // namespace + +TEST(WrapTest, Ptr_to_member) +{ + TestAddition obj; + TestWrapperType::wrapper_type wrapped = TestWrapperType::Wrap(&TestAddition::AddNumbers, "a", "b"); + + MockDataSource mockSrc; + EXPECT_CALL(mockSrc, GetKeys()).WillRepeatedly(Return(std::vector{"a", "b"})); + EXPECT_CALL(mockSrc, Get("a", An())).WillRepeatedly(SetArgReferee<1>(42)); + EXPECT_CALL(mockSrc, Get("b", An())).WillRepeatedly(SetArgReferee<1>(21)); + + flexible_value returnValue = wrapped(obj, mockSrc); + EXPECT_EQ(returnValue.as(), 84); +} + +TEST(WrapTest, Const_Ptr_to_member) +{ + const TestAddition obj; + TestConstWrapperType::wrapper_type wrapped = TestConstWrapperType::Wrap(&TestAddition::AddNumbersConst, "a", "b"); + + MockDataSource mockSrc; + EXPECT_CALL(mockSrc, GetKeys()).WillRepeatedly(Return(std::vector{"a", "b"})); + EXPECT_CALL(mockSrc, Get("a", An())).WillRepeatedly(SetArgReferee<1>(42)); + EXPECT_CALL(mockSrc, Get("b", An())).WillRepeatedly(SetArgReferee<1>(21)); + + flexible_value returnValue = wrapped(obj, mockSrc); + EXPECT_EQ(returnValue.as(), 84); +} + +TEST(WrapTest, Const_Ptr_to_member_as_non_const) +{ + const TestAddition obj; + TestConstWrapperType::wrapper_type wrapped = TestConstWrapperType::Wrap(&TestAddition::AddNumbersConst, "a", "b"); + + MockDataSource mockSrc; + EXPECT_CALL(mockSrc, GetKeys()).WillRepeatedly(Return(std::vector{"a", "b"})); + EXPECT_CALL(mockSrc, Get("a", An())).WillRepeatedly(SetArgReferee<1>(42)); + EXPECT_CALL(mockSrc, Get("b", An())).WillRepeatedly(SetArgReferee<1>(21)); + + flexible_value returnValue = wrapped(obj, mockSrc); + EXPECT_EQ(returnValue.as(), 84); +} diff --git a/empty.cpp b/empty.cpp new file mode 100644 index 0000000..e69de29