From 048ac71f316194a85a01ad8684093c2fcf61b8ce Mon Sep 17 00:00:00 2001 From: Per Ryan Edin Date: Thu, 4 Jun 2026 03:08:51 +0200 Subject: [PATCH] Launch in T minus 10 seconds and counting... --- .editorconfig | 14 ++ .gitattributes | 1 + .gitignore | 38 +++++ LICENSE | 31 ++++ README.md | 28 ++++ build/.gitignore | 4 + build/bootstrap.build | 7 + build/export.build | 6 + build/root.build | 16 ++ buildfile | 5 + framework/.gitignore | 9 ++ framework/buildfile | 58 +++++++ framework/type-list.hxx | 304 ++++++++++++++++++++++++++++++++++++ framework/types.hxx | 41 +++++ framework/version.hxx.in | 37 +++++ manifest | 12 ++ repositories.manifest | 2 + tests/.gitignore | 8 + tests/build/.gitignore | 4 + tests/build/bootstrap.build | 6 + tests/build/root.build | 16 ++ tests/buildfile | 1 + 22 files changed, 648 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 build/.gitignore create mode 100644 build/bootstrap.build create mode 100644 build/export.build create mode 100644 build/root.build create mode 100644 buildfile create mode 100644 framework/.gitignore create mode 100644 framework/buildfile create mode 100644 framework/type-list.hxx create mode 100644 framework/types.hxx create mode 100644 framework/version.hxx.in create mode 100644 manifest create mode 100644 repositories.manifest create mode 100644 tests/.gitignore create mode 100644 tests/build/.gitignore create mode 100644 tests/build/bootstrap.build create mode 100644 tests/build/root.build create mode 100644 tests/buildfile diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4a6ccd4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +indent_size = 4 +max_line_length = off +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3602660 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +.bdep/ + +# Local default options files. +# +.build2/local/ + +# Compiler/linker output. +# +# Note that *.exe.dlls directories can be symlinks thus +# no trailing /. +# +*.d +*.t +*.i +*.i.* +*.ii +*.ii.* +*.o +*.obj +*.gcm +*.pcm +*.ifc +*.so +*.dylib +*.dll +*.a +*.lib +*.exp +*.pdb +*.ilk +*.exe +*.exe.dlls +*.exe.manifest +*.pc + +# Compilation database. +# +compile_commands.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..862cbe2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,31 @@ +Copyright © 2026 Ryan. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. All advertising materials mentioning features or use of this software must + display the following acknowledgement: + + This product includes software developed by Ryan, http://helloryan.se/. + +4. Neither the name(s) of the copyright holder(s) nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..bfc28e5 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +framework +========= + +Copyright © 2026 Per Ryan Edin. + +framework is a library containing reusable code that I use in many of my other +projects. As I grew tired of rewriting boilerplate I began moving things to +this library instead. + +framework is intended for use in my own projects, it is not to be considered a +general-purpose framework library. If you do find this library useful you're +welcome to let me know by sending me an e-mail to ryan@helloryan.se. + +There are no API or ABI stability guarantees. + +See LICENSE for copying details. + +Nasty little fuckers... +======================= + +Please report any sightings of nasty creatures by sending an e-mail to +ryan@helloryan.se. + +Building +======== + +framework uses the build2 build system. There are no dependencies other than the +standard C++ library. diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 0000000..974e01d --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,4 @@ +/config.build +/root/ +/bootstrap/ +build/ diff --git a/build/bootstrap.build b/build/bootstrap.build new file mode 100644 index 0000000..1bf5f21 --- /dev/null +++ b/build/bootstrap.build @@ -0,0 +1,7 @@ +project = framework + +using version +using config +using test +using install +using dist diff --git a/build/export.build b/build/export.build new file mode 100644 index 0000000..96ed340 --- /dev/null +++ b/build/export.build @@ -0,0 +1,6 @@ +$out_root/ +{ + include framework/ +} + +export $out_root/framework/$import.target diff --git a/build/root.build b/build/root.build new file mode 100644 index 0000000..2767c48 --- /dev/null +++ b/build/root.build @@ -0,0 +1,16 @@ +# Uncomment to suppress warnings coming from external libraries. +# +cxx.internal.scope = current + +cxx.std = latest + +using cxx + +hxx{*}: extension = hxx +ixx{*}: extension = ixx +txx{*}: extension = txx +cxx{*}: extension = cxx + +# The test target for cross-testing (running tests under Wine, etc). +# +test.target = $cxx.target diff --git a/buildfile b/buildfile new file mode 100644 index 0000000..9a5145b --- /dev/null +++ b/buildfile @@ -0,0 +1,5 @@ +./: {*/ -build/} doc{README.md} manifest + +# Don't install tests. +# +tests/: install = false diff --git a/framework/.gitignore b/framework/.gitignore new file mode 100644 index 0000000..b1ed0e0 --- /dev/null +++ b/framework/.gitignore @@ -0,0 +1,9 @@ +# Generated version header. +# +version.hxx + +# Unit test executables and Testscript output directories +# (can be symlinks). +# +*.test +test-*.test diff --git a/framework/buildfile b/framework/buildfile new file mode 100644 index 0000000..7646e3b --- /dev/null +++ b/framework/buildfile @@ -0,0 +1,58 @@ +intf_libs = # Interface dependencies. +impl_libs = # Implementation dependencies. + +./: lib{framework}: libul{framework} + +libul{framework}: {hxx ixx txx cxx}{** -**.test... -version} \ + {hxx }{ version} + +libul{framework}: $impl_libs $intf_libs + +# Unit tests. +# +exe{*.test}: +{ + test = true + install = false +} + +for t: cxx{**.test...} +{ + d = $directory($t) + n = $name($t)... + + ./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n} + $d/exe{$n}: libul{framework}: bin.whole = false +} + +hxx{version}: in{version} $src_root/manifest + +# Build options. +# +cxx.poptions =+ "-I$out_root" "-I$src_root" + +# Export options. +# +lib{framework}: +{ + cxx.export.poptions = "-I$out_root" "-I$src_root" + cxx.export.libs = $intf_libs +} + +# For pre-releases use the complete version to make sure they cannot +# be used in place of another pre-release or the final version. See +# the version module for details on the version.* variable values. +# +if $version.pre_release + lib{framework}: bin.lib.version = "-$version.project_id" +else + lib{framework}: bin.lib.version = "-$version.major.$version.minor" + +# Install into the framework/ subdirectory of, say, /usr/include/ +# recreating subdirectories. +# +{hxx ixx txx}{*}: +{ + install = include/framework/ + install.subdirs = true +} diff --git a/framework/type-list.hxx b/framework/type-list.hxx new file mode 100644 index 0000000..975d507 --- /dev/null +++ b/framework/type-list.hxx @@ -0,0 +1,304 @@ +#ifndef framework__type_list_hxx_ +#define framework__type_list_hxx_ + +#include + +namespace framework +{ + + /// @brief Type list. + /// + /// The type_list class serves two purposes. It functions as a template + /// meta-programming list of types (Ts) as well as a container of instances of + /// those types. + /// + template + class type_list + { + public: + /// @brief Tag-dispatch type. + /// + struct owning_t {}; + + /// @brief Tag for creating an owning type_list. + /// + inline static constexpr owning_t owning{}; + + /// @brief Constructor. + /// + /// This constructor creates an owning type list. + /// + /// @param args The arguments passed to the constructor of the Ts. + /// + /// + template + explicit + type_list(owning_t const&, Args&&... args) + : _data{make_unique(std::forward(args)...)...} + {} + + /// @brief Constructor. + /// + /// This constructor makes a non-owning copy of @a other. + /// + /// Once @a other is destroyed, this type list becomes invalidated. + /// + type_list(type_list& other) + : _data{variant_type{&other.template get()}...} + {} + + /// @brief Constructor. + /// + /// This constructor makes a non-owning filtered copy of @a other. + /// + /// Once @a other is destroyed, this type list becomes invalidated. + /// + template + type_list(type_list& other) + : _data{variant_type{&other.template get ()}...} + {} + + /// @brief Constructor. + /// + /// Moves @a other into this type list. + /// + type_list(type_list&& other) + : _data{std::move(other._data)} + {} + + /// @brief Get instance of type T held in this type list. + /// + template + T& + get() + { + auto& var = std::get>(_data); + + return std::visit( + [](auto&& val) -> T& + { + return *val; + }, + var + ); + } + + /// @brief Get instance of type T held in this type list. + /// + template + T const& + get() const + { + auto& var = std::get>(_data); + + return std::visit( + [](auto&& val) -> T const& + { + return *val; + }, + var + ); + } + + /// @brief Apply (invoke) function @a f passing all @a Ts and @a args. + /// + template + decltype(auto) + apply(F&& f, Args&&... args) + { + return f.template operator()( + get()..., + std::forward(args)... + ); + } + + /// @brief Apply (invoke) function @a f passing all @a Ts and @a args. + /// + template + decltype(auto) + apply(F&& f, Args&&... args) const + { + return f.template operator()( + get()..., + std::forward(args)... + ); + } + + /// @brief Apply (invoke) function @a f once for each @a Ts passing all @a + /// args. + /// + template + void + apply_for_each(F&& f, Args&&... args) + { + ( + f.template operator()( + get(), + std::forward(args)... + ), + ... + ); + } + + /// @brief Apply (invoke) function @a f once for each @a Ts passing all @a + /// args. + /// + template + void + apply_for_each(F&& f, Args&&... args) const + { + ( + f.template operator()( + get(), + std::forward(args)... + ), + ... + ); + } + + template + static + decltype(auto) + apply_types(F&& f, Args&&... args) + { + return f.template operator()( + std::forward(args)... + ); + } + + template + static + decltype(auto) + apply_types_for_each(F&& f, Args&&... args) + { + ( + ( + f.template operator()( + std::forward(args)... + ) + ), + ... + ); + } + + private: + /// @brief Variant type for either owning or borrowing type T. + /// + template + using variant_type = variant, T*>; + + /// @brief Tuple type. + /// + using tuple_type = tuple...>; + + /// @brief Tuple holding instances of Ts. + /// + tuple_type _data; + + }; + + /// @brief Template to check if @a T is a type list. + /// + template + struct is_type_list + : std::false_type + {}; + + template + struct is_type_list> + : std::true_type + {}; + + /// @brief Helper for is_type_list. + /// + template + inline constexpr bool const is_type_list_v = is_type_list::value; + + /// @brief Type list concept. + /// + template + concept TypeList = is_type_list_v; + + /// @brief Template to concatenate two or more type lists. + /// + template + struct concat_type_list; + + template<> + struct concat_type_list<> + { + using type = type_list<>; + }; + + template + struct concat_type_list> + { + using type = type_list; + }; + + template + struct concat_type_list< + type_list, + type_list, + Rest... + > + { + using type = typename concat_type_list< + type_list, Rest... + >::type; + }; + + /// @brief Helper for concat_type_list. + /// + template + using concat_type_list_t = typename concat_type_list::type; + + /// @brief Template to filter type list. + /// + template< + template typename Predicate, + TypeList List + > + struct filter_type_list; + + template typename Predicate, typename... Ts> + struct filter_type_list> + { + using type = concat_type_list_t< + std::conditional_t< + Predicate::value, + type_list, + type_list<> + >... + >; + }; + + /// @brief Helper for filter_type_list. + /// + template typename Predicate, typename List> + using filter_type_list_t = typename filter_type_list::type; + + /// @brief Template to check if type T has T::deps and that T::deps is a + /// type list. + /// + template + struct has_deps + : std::false_type + {}; + + template + struct has_deps> + : std::integral_constant< + bool, + is_type_list_v + > + {}; + + /// @brief Helper for has_deps. + /// + template + inline constexpr bool const has_deps_v = has_deps::value; + +} // namespace framework + +#endif diff --git a/framework/types.hxx b/framework/types.hxx new file mode 100644 index 0000000..dfe704f --- /dev/null +++ b/framework/types.hxx @@ -0,0 +1,41 @@ +#ifndef framework__types_hxx_ +#define framework__types_hxx_ + +#include +#include +#include +#include + +/// @brief Main namespace. +/// +namespace framework +{ + + /// @brief Namespace with common types. + /// + namespace types + { + + using std::int8_t; + using std::int16_t; + using std::int32_t; + using std::int64_t; + + using std::uint8_t; + using std::uint16_t; + using std::uint32_t; + using std::uint64_t; + + using std::unique_ptr; + using std::make_unique; + + using std::tuple; + using std::variant; + + } // namespace types + + using namespace types; + +} // namespace framework + +#endif diff --git a/framework/version.hxx.in b/framework/version.hxx.in new file mode 100644 index 0000000..634d2d5 --- /dev/null +++ b/framework/version.hxx.in @@ -0,0 +1,37 @@ +#ifndef framework__version_hxx_ +#define framework__version_hxx_ + +// The numeric version format is AAAAABBBBBCCCCCDDDE where: +// +// AAAAA - major version number +// BBBBB - minor version number +// CCCCC - bugfix version number +// DDD - alpha / beta (DDD + 500) version number +// E - final (0) / snapshot (1) +// +// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example: +// +// Version AAAAABBBBBCCCCCDDDE +// +// 0.1.0 0000000001000000000 +// 0.1.2 0000000001000020000 +// 1.2.3 0000100002000030000 +// 2.2.0-a.1 0000200001999990010 +// 3.0.0-b.2 0000299999999995020 +// 2.2.0-a.1.z 0000200001999990011 +// +#define FRAMEWORK_VERSION $framework.version.project_number$ULL +#define FRAMEWORK_VERSION_STR "$framework.version.project$" +#define FRAMEWORK_VERSION_ID "$framework.version.project_id$" +#define FRAMEWORK_VERSION_FULL "$framework.version$" + +#define FRAMEWORK_VERSION_MAJOR $framework.version.major$ +#define FRAMEWORK_VERSION_MINOR $framework.version.minor$ +#define FRAMEWORK_VERSION_PATCH $framework.version.patch$ + +#define FRAMEWORK_PRE_RELEASE $framework.version.pre_release$ + +#define FRAMEWORK_SNAPSHOT_SN $framework.version.snapshot_sn$ULL +#define FRAMEWORK_SNAPSHOT_ID "$framework.version.snapshot_id$" + +#endif diff --git a/manifest b/manifest new file mode 100644 index 0000000..ba964dd --- /dev/null +++ b/manifest @@ -0,0 +1,12 @@ +: 1 +name: framework +version: 0.1.0-a.0.z +type: lib +language: c++ +summary: framework C++ library +license: other: proprietary ; Not free/open source. +description-file: README.md +url: https://example.org/framework +email: ryan@helloryan.se +depends: * build2 >= 0.18.0 +depends: * bpkg >= 0.18.0 diff --git a/repositories.manifest b/repositories.manifest new file mode 100644 index 0000000..2ec7cea --- /dev/null +++ b/repositories.manifest @@ -0,0 +1,2 @@ +: 1 +summary: framework project repository diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..662178d --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,8 @@ +# Test executables. +# +driver + +# Testscript output directories (can be symlinks). +# +test +test-* diff --git a/tests/build/.gitignore b/tests/build/.gitignore new file mode 100644 index 0000000..974e01d --- /dev/null +++ b/tests/build/.gitignore @@ -0,0 +1,4 @@ +/config.build +/root/ +/bootstrap/ +build/ diff --git a/tests/build/bootstrap.build b/tests/build/bootstrap.build new file mode 100644 index 0000000..2a457a8 --- /dev/null +++ b/tests/build/bootstrap.build @@ -0,0 +1,6 @@ +project = # Unnamed tests subproject. + +using version +using config +using test +using dist diff --git a/tests/build/root.build b/tests/build/root.build new file mode 100644 index 0000000..a67b2fe --- /dev/null +++ b/tests/build/root.build @@ -0,0 +1,16 @@ +cxx.std = latest + +using cxx + +hxx{*}: extension = hxx +ixx{*}: extension = ixx +txx{*}: extension = txx +cxx{*}: extension = cxx + +# Every exe{} in this subproject is by default a test. +# +exe{*}: test = true + +# The test target for cross-testing (running tests under Wine, etc). +# +test.target = $cxx.target diff --git a/tests/buildfile b/tests/buildfile new file mode 100644 index 0000000..aeeab15 --- /dev/null +++ b/tests/buildfile @@ -0,0 +1 @@ +./: {*/ -build/}