Giter Site home page Giter Site logo

guillaumedua / cppshelf Goto Github PK

View Code? Open in Web Editor NEW
9.0 9.0 0.0 10.04 MB

Collection of powerfuls - C++ Single-Header Libraries Files

Home Page: https://guillaumedua.github.io/CppShelf/

License: MIT License

CMake 1.72% C++ 98.28%
cpp cpp20 metaprogramming

cppshelf's Introduction

๐ŸŽ† About me: Guillaume Dua ๐ŸŽ†

Note: this is my personnal - thus, not professional - account.


Github stats Most used languages


  • ๐Ÿ’ก I love post-modern ๐ŸŒŸ C++ ๐ŸŒŸ. Like really. Semantics & expressiveness in general, template-metaprogramming in particular.
  • ๐Ÿ’ป I work as a C++ trainer & expert, and lead software architect.
  • ๐ŸŒ  I always enjoy spending some of my freetime on Github, check out my repos ! ๐Ÿ’–
  • ๐ŸŽค๐Ÿ™‹ I also love participating in conferences, as a speaker or listener.

cppshelf's People

Contributors

guillaumedua avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

cppshelf's Issues

[wf] Add TTPS feature in syntactic sugar (operators overload)

[wf] Add TTPS feature in syntactic sugar (operators overload)

Related to :

Details

The following synthaxe should be valid :

auto route = [](){ return 42; }
   >>= mp::ttps<int, char>{}
   >>= []<typename T, typename U>(int){
       static_assert(std::same_as<int, T>);
       static_assert(std::same_as<char, U>);
   }
;

[wf] consider using P1985 "Universal template parameters" to extend invoke API

[wf] consider using P1985 "Universal template parameters" to extend invoke API

Prerequisites

C++23 ?

Motivation

Currently (12/03/2021), NTTPs are not supported by the current API,
thus have to be passed as TTPs using for instance std::integral_constant, so true becomes std::true_type

As-is, this result in counter-intuitive syntaxes, considered as usage impediments.

Example

APIs might looks like :

template <template auto ... ttps, typename ... args_t>
decltype(auto) csl::wf::invoke(args_t && ... args);

[wf] csl::wf::front_binder : type_erasure

[wf] csl::wf::front_binder : type_erasure

  • simplify front_binder with some semi-type-erasure

    // definition
    front_binder<F, ...>; // non-mandatory ttps, args
    
    front_binder::front_binder(auto && ...);
    // internal storage for bound elements become a type-erased lambda with variadic capture group,  
    // into an std::function<void(F)>, to allow some visitation pattern ?

misc

  • front_binder<F, ttps<...>, args<...>> :

    class front_binder<F, mp::ttps<ttps_bounded_args_t...>, mp::args<bounded_args_t...>>;
    //                     ^^^^^                             ^^^^^^

[wf] syntactic sugar : explicit `discard` parameters for route

[wf] syntactic sugar : explicit discard parameters for route

Related to :

Details

The following syntaxes must be correct :

  • discard_args / drop / take
    auto route = [](){ return 21; } >>= discard_args >>= [](){};
    auto route = [](){ return 21; } >>= discard_args >>= [](int i = 42){ /* i == 42 */ };
  • Using #24 mp::args
    auto route = [](){ return 21; } >>= discard_args >>= mp::args{ 42 } >>= [](int i){ /* i == 42 */ };
    which is equivalent to :
    auto route = [](){ return 21; } >>= mp::args{ 42 } >>= [](int i){ /* i == 42 */ };

as well as :

  • mp::discard_n_args<N> / mp::drop_n_args<2>
    auto route =   [](){ return 42; } >>= mp::args_before{ 'a', 'b', 'c' } >>= mp::drop_n_args<2> >>= [](char c, int i){
      // c == 'c'
      // i == 42
    };

[wf] : Consider using P2347 to deduce a single non-trailing parameters pack for function arguments

csl::wf : Consider using P2347 to deduce a single non-trailing parameters pack for function arguments

Prerequisites

  • C++26

Evolutions

Consider P2347 "Argument type deduction for non-trailing parameter packs" to deduce a single non-trailing parameters pack for function arguments

Example

template <typename auto ... n_ttps>
csl::wf::bind_invoke([](auto&&... args) {
   // invocation
}, fwd(bound_args)..., fwd(args)..., marker<>{});

[wf] execution policies

csl::wf : execution policies

Motivation

Consider adding an execution policy matrix to chain invocation

  • Standard vs nodiscard-guarantee
  • Complete or N nodes
  • Sequential vs parallel ? (see #29)

[wf] Improve csl::wf::front_binder

[wf] Improve csl::wf::front_binder

Motivation

Extend the interface of front_binder so it becomes more user-friendly.

Also, only bind_front is covered with tests, not front_binder itself.
As such type is exposed publicly, it should be tested as well in UTs.

Details

New member-functions

  • (conditionally trivial) copy constructor
  • (conditionally trivial) move constructor
  • (conditionally trivial) copy operator=
  • (conditionally trivial) move operator=
  • comparisons operator ==, not_eq

Enhancement of existing member-functions

  • (conditionally noexcept) constructors

Misc

Also consider some binding similar to std::bind/std::bind_front

  • Member-variable binding

Dev branch : enhancement/front_binder

[srl] Serialization library

[srl] Serialization library

Motivation

Based on gcl::serialization for type-indexing, but with customisation points (and still default behaviors).

API

TBD

  • serialize<T>
  • deserialize_as<T>
  • deserialize (gcl::serialization's reader)

Customization point

Based on the detection-idiom :

  • serializable/not_serializable

  • Descriptor :

    template-type-parameter-list <members to invoke...>
    (deserialization reverse the order)

[repo/CI] Use dockerfiles with embedded toolchain

[repo/CI] Use dockerfiles with embedded toolchain

  • TODO: better branches trigger policies:
    • triggers on pull-request (all branches)
    • triggers on push (main)

Motivations

  • In order to avoid Github-Action to fail to connect to LLVM repos.
  • Faster CI
  • More resilience to compilers updates

Result

https://hub.docker.com/repository/docker/gussd/cpp-toolchain/general

Q: Can be based on https://github.com/jfalcou/compilers/blob/main/Dockerfile ?

Add an overload that add the following features (release as options) :

  • GCC
  • LLVM toolsuite (Clang, lldb, libc++, clang-tidy, clang-format, etc. )
  • CMake

Q: Can we add ranges of release/version for theses packages,
with updates-alternatives (the latest stable release the better ?)

Q: as a script, invoked in the DockerFile ?

vscode - devcontainer

create CMake toolchains for such common configurations

  • latest gcc
  • latest clang
    • with libc++
    • with libstdc++ (latest gcc)

CI - simplication


Related issues/requests :

[wf] syntactic sugar : additional parameters for route

[wf] additional parameters for route

Related to :

Details

The following syntaxes should be correct :

  • mp::args
using namespace csl::wf;
auto route = [](){}
   >> mp::args{ 42 }
   >> [](int i){}
;

See #25 for more details about mp::args behavior here

  • mp::args_before
using namespace csl::wf;
auto route = [](){ return std::string{}; }
   >> mp::args_before{ 42 }
   >> [](int, std::string){}
;
  • mp::args_after
using namespace csl::wf;
auto route = [](){ return std::string{}; }
   >> mp::args_after{ 42 }
   >> [](std::string, int){}
;

[wf] binder : simplify storage

[wf] binder : simplify storage

Can be linked to #37

Description

Move F as part of bounded_args internal storage tuple.

Use std::get<0> to access

Misc

Accessor for F

csl::wf (workflow) : add syntactic sugar / basic eDSL

csl::wf (workflow) : add syntactic sugar / eDSL

Then / continuation

auto f1_then_f2_then_f3 =
   f1 >>= f2 >>= f3;

branching - based on return type

auto f1_then_either_f2_f3_or_f4 = f1 >>= (f2 | f3 | f4);

repetition / times

auto f1_3_times = f1 * 3;
auto f1_3_times = f1 * std::integral_constant<std::size_t, 3>;

parallelization (nice-to-have, requires more design, lifetime management, etc.)

Requires extension for concurrency

auto f1_and_f2 = f1 || f2;

// design 1 : synchronized join
auto route = f1_and_f2 >>= f3;
route(); // csl::wf::invoke(f3, std::future::when_all(f1_future, f2_future));

// design 2 :
auto route = f1_and_f2 |= f3;
route(); // csl::wf::invoke(f3, std::future::when_any(f1_future, f2_future)); 

[srl] tests utility

[srl] tests utility

Compile-time io-stream (read/write interface) for constexpr-dependent context testing

Motivation

CppShelf testing is compile-time.

[wf] format eDSL : format("<expr>", Fs...)

csl::wf : format eDSL

  • Compile-time/run-time format string that can be imported from external source (configuration file, network, etc.)
  • constexpr parsing / check for valid expression
  • Similar syntaxe to syntactic sugar (operators), with placeholders

Motivation example

format("<expr>", Fs...);

Syntaxe hypothesis

auto route = a >>= (b | c) >>= d;
// equivalent to
auto route = csl::wf::parse(
    "{} >>= ({} | {}) >>= {}",
    a,b,c,d
);
// and
auto route = csl::wf::parse(
    "{a} >>= ({b} | {c}) >>= {d}",
    "a"_a_value,
    "b"_b_value,
    "c"_c_value,
    "d"_d_value,
);  

[wf] : function_view : non-owning/view for functors

[wf] : function_view : non-owning/view for functors

Motivation :

  • Costly functors values that should not be copied, yet not moved either ?
    In such case, copy_constructible is not the requirement for F
  • In opposition to RAII in csl::wf::front_binder

And more intuitive & obvious resources acquisition / ownership / lifetime

Solution :

Motivation synthaxe :

auto f1_then_f2 = f1 >>= not_owning{ f2 };
// or
auto f1_then_f2 = f1 >>= view | f2;

[wf] type-erasure

[wf] type-erasure

In opposition to the way std::function or std::any_invocable are designed,
create some wrapper that conditionaly expose any operator()

details

namespace csl::wf {
   // not template
   struct function {
      template <typename ...>
      operator()();
   };
}

csl::wf : Better/less (compile-time) error messages

csl::wf : Better/less (compile-time) error messages

Goal

Add more user-friendly error at compile-time, ratherthan long C++ tmp errors-bloat messages.

How

  • Stop compiling as soon as possible
  • Pedagogical errors

[ag] Aggregate-get

[ag] Aggregate-get

Allow by-default std::get<index> on any aggregate type.

Can improve csl::srl, providing a better - yet default - way to (un)serialize aggregates by accessing members using the tuple-interface.

poc/wip

https://godbolt.org/z/7oo3j6dxq

What remains to do ?

Hypothetic implementation relying on P1061 - Structured Bindings can introduce a Pack

// test using structured-binding with `field_counts` elements ( `== sizeof...(fields_value)`)
// is this expression valid ?
auto [ fields_value... ] = std::declval<T>();
// if so, tuple-like type using `tie`-like function, or
using type = aggregate_tuple_accessor<
      std::remove_cvref_t<decltype(fields_value)>
>;
return type{ std::move(fields_value)... };

Other research trails

struct toto{ int i; char c; };
using accessors = aggregate_accessors<toto>;

static_assert(std::same_as<
   accessors,
   csl::srl::descriptions::aggregate_initialization<
        &test::tutu::get_i,
        &test::tutu::get_c
    >
>);

// injection for csl::srl as description-type
namespace csl::srl {
    template <Aggregate T>
    struct description<T> {
        using type =  aggregate_accessors<T>;
    };
}

[wf] Make invocation utilities available as a stand-alone library

[wf] Make invocation utilities available as a stand-alone library

As addition and/or replacement to STL's

Details

Invocation

  • csl::wf::invoke
  • csl::wf::apply

Type-traits

  • csl::wf::is_invocable

  • csl::wf::is_nothrow_invocable

  • csl::wf::invoke_result

  • csl::wf::is_applyable

  • csl::wf::is_nothrow_applyable

  • csl::wf::apply_result

[wf] Remove recursivity for `chain_invoke`, proper requirements

[wf] Remove recursivity for chain_invoke

  • Using a dedicated operator so fold expression can replace recursivity, even for dependent expansion

POC : https://godbolt.org/z/8noTGTvqa

namespace details::invocation_literals::eval {
    // detection/evaluation only
    constexpr auto operator>>(auto && args, auto && f)
    noexcept (std::is_nothrow_invocable_v<decltype(f), decltype(fwd(args))>)
    -> std::invoke_result_t<decltype(f), decltype(args)>
    requires (std::invocable<decltype(f), decltype(fwd(args))>)
    ; // for evaluation only, never defined
}
namespace details::invocation_literals {
    // do the job
    constexpr auto operator>>=(auto && args, auto && f)
    noexcept (std::is_nothrow_invocable_v<decltype(f), decltype(fwd(args))>)
    {
        return std::invoke(fwd(f), fwd(args));
    }
}

namespace details::mp {
    template <typename Fs, typename Args>
    struct is_chain_nothrow_invocable {
        constexpr static bool value = []<std::size_t ... indexes>(std::index_sequence<indexes...>) {
            using namespace details::invocation_literals::eval;
            return noexcept((std::declval<Args>() >> ... >> std::get<indexes>(std::declval<Fs>())));
        }(std::make_index_sequence<std::tuple_size_v<std::remove_cvref_t<Fs>>>{});
    };
}

[mp] csl::mp::depth_of

mp::depth_of

Get the depth of a template instanciation

Motivation

  • Promote type-correctness in contexts like types-flattening
  • Reduce output type complexity
  • Promote cleaner error messages

Expected output

using type = int;
static_assert(0 == csl::mp::depth_of_v<type>);
using type = std::tuple<int>;
static_assert(1 == csl::mp::depth_of_v<type>);
using type = std::tuple<std::tuple<int>>;
static_assert(2 == csl::mp::depth_of_v<type>);

[wf] Better error message when both ttps passing strategies are used

[wf] Better error message when both ttps passing strategies are used

Context :

  • Currently, ttps can be passed to most functions/types either as ttps, or as first parameter of type mp::ttps<ts...>

Problem :

  • What is the expected behavior if both technics are used at the same time ?

Current behavior :

   auto func = []<typename T>(auto && value){
        static_assert(std::same_as<T, int>);
        static_assert(std::same_as<std::remove_cvref_t<decltype(value)>, char>);
    };

    // OK :
    func.template operator()<int>('a');
    csl::wf::invoke<int>(func, 'a');
    csl::wf::invoke(func, csl::wf::mp::ttps<int>{}, 'a');

    // OK, redundant ttps
    csl::wf::invoke<int>(func, csl::wf::mp::ttps<int>{}, 'a');

    // KO : mismatch ttps
    csl::wf::invoke<bool>(func, csl::wf::mp::ttps<int>{}, 'a');
    csl::wf::invoke<int>(func, csl::wf::mp::ttps<bool>{}, 'a');

Solution :

  • Use a custom error message, with a dedicated overload candidate to prevent using both ttps-passing strategies

or

  • Use a custom error message, with a dedicated overload candidate to ensure both ttps are the same

Should `wf` use `mp` ?

Should wf use mp ?

csl::wf could use :

  • csl::mp::flatten<Ts...>
  • csl::mp::deduplicate_t<Ts...>

Pro :

So there won't be any duplicated code in wf and mp.

Cons :

Libraries won't be single-files/independent

Workflow : Consider changing `function_traits` interface

Workflow : Consider changing function_traits interface

Dependency of #3 ?


Take is_invocable for instance. Handling two parameter-pack (which are function ttps and args) is not convenient,
thus create an error-prone context.

Before :

  • An optional first parameter represents the function's ttps.
    template <typename F, typename ... args_t>
    constexpr bool is_invocable_v = std::is_invocable_v<F, args_t...>;
    template <typename F, typename ... args_t>
    constexpr bool is_invocable_v<F, ttps_pack<>, args_t...> =
        std::is_invocable_v<F, args_t...>;
    ;
    template <typename F, typename ... ttps_args_t, typename ... args_t>
    constexpr bool is_invocable_v<F, ttps_pack<ttps_args_t...>, args_t...> =
        requires{ std::declval<F>().template operator()<ttps_args_t...>(std::declval<args_t>()...); }
    ;

Proposal :

  • Make a clear separation between ttps and args.
   template <typename F, typename ttps, typename args>
    struct invocable {
        static_assert([](){ return false; }(), "parameters must be wrapped in `pack`");
    };
    template <typename F, typename ... ttps, typename ... args>
    struct invocable<F, pack<ttps...>, pack<args...>> {
        constexpr static bool value = requires{
            std::declval<F>().template operator()<ttps...>(std::declval<args>()...);
        };
    };
    template <typename F, typename ... args>
    struct invocable<F, pack<>, pack<args...>> {
        constexpr static bool value = std::is_invocable_v<F, args...>;;
    };
    template <typename F, typename ttps, typename args>
    constexpr bool is_invocable_v = invocable<F, ttps, args>::value;

    consteval void test() {
        const auto f_witht_ttps = []<typename T>(bool, int){};
        static_assert(is_invocable_v<decltype(f_witht_ttps), pack<int>, pack<bool, int>>);

        const auto f_void = []<typename T>(){};
        static_assert(    is_invocable_v<decltype(f_void), pack<int>, pack<>>);
        static_assert(not is_invocable_v<decltype(f_void), pack<int>, pack<void>>);
        static_assert(    is_invocable_v<decltype(f_void), pack<int>, details::skip_void_t<pack<void>>>);
    }

See poc at https://godbolt.org/z/4c674j6Pj.

Pros :

  • Makes errors less likely in a way.
  • Easier application of pack_traits over both ttps (no need for additional wrap/unwrap / fold/unfold)

Cons :

  • Syntax less intuitive, as more different from STL's
    • Before : one extra - but optional - parameter
    • After : requires to wrap both parameter-packs in pack

[wf] bind_back

[wf] bind_back

Similar to bind_front, but expand ttps and args after operator() parameters.

Like std::bind_back, but according to the csl::wf design philosophy.

[ag] remove preprocessor macros

[ag] remove preprocessor macros

Part of #50

ag.cmake should only generated C++ code, not preprocessor macros

Motivations

Make the project easier to debug, rather than forcing user to use external tools or additional compiler options to expand preprocessor macros.

  • pro : simpler code
  • con : longer code

[Repo/GCC] Add .vscode for quick edit/build

[Repo/GCC] Add .vscode for quick edit/build

  • Should work for most/any Linux using GCC/G++

    (as currently, this is the compiler that provides the most C++20 features)

  • Should use default /usr/bin/g++ path
  • Should allow the user to build a single file, using preprocessor define DCPP_SHELVE_STANDALONE_EDIT__
  • Must work on WSL

[ag] tuple-like interface for aggregate

[ag] tuple-like interface for aggregate

Requirement for #50


template <std::size_t fields_count> // result of fields_count<T>
auto as_tuple(Aggregate auto && value) {
     auto & [ /* id_ ... */ ] = value;
     return std::tuple</* of references */>{ /* id_ ... */ };
}

Context

Discussion on SO

What remains to do ?

As P1061 is not available yet, the only remaining options seems to generate the C++ code - e.g as_tuple specialization using some cmake/python scripts at configure time.

Acceptance criterias

  • Tests

Workflow : chained invocation

Chained invocation

Consider the following design for implementation : https://godbolt.org/z/M59ob4z45

Remaining to do :

  • cvref-qualifiers propagation
  • Integration TTPS propagation
    • Consider handling TTPS only for the first node of the chain (others must be deductible using overload)
  • noexcept propagation
  • Clear / pedagogical errors
  • constexpr

Acceptance criteria

  • Ensure no wrong wiring can be call
  • Ensure no copies are made
  • Ensure the resulting invocation (using compilers optimization level) does not result in additional code assembly
  • Ensure there are no storage / mutability / constness violation

(Abandoned) Alternatives :

Most does not scale enough

[wf] chain_trait, chain_invoke : `mp::ttps` in args should never be discardable

[wf] chain_trait, chain_invoke : mp::ttps in args should never be discardable

Context :

  • As is, args are discardable while performing a not-nodiscard invocation,
    meaning that :

    [](){ return int{}; } >>= [](){}; // discard the node_1 return value

    is a legal synthax

Improvement :

  • At least for the chain entry point, avoid mp::ttps<Ts...> to be potentionaly discarded.

[wf] `circle` compiler for earlier C++23 features access

[wf] circle compiler to get P0847R5 (deducting this)

https://www.circle-lang.org/

Motivation

Existing code simplification using C++23 features.

Both can greatly ease the implementation of various csl::wf components, especially the ones which have multiples operator() overloads to preverse cvref-qualifiers correctness, or are restricted to ttps only (nttps not supported yet).

What remains to do ?

  • Github-Actions yml file that configures, builds, tests using circle compiler
  • Separated implementation for C++23-relying features

[repo] CMake support

Add CMake support for CSL

Each component as independent INTEFACE library

  • csl::wf
  • csl::workflow

As well as all regrouped into on INTERFACE library, csl::csl

Take best-practices from gcl

Add graphviz into readme

Workflow : chain_trait

Workflow : chain_trait

Dependency of :

Depends on :


Consider using a chain_trait to split the detection/infos logic of chains,
https://godbolt.org/z/YjovqK4ds

  • args_type == void
  • nothrow
  • TTPS
  • invocation flow (not part of trait)
  • Tests -> static_asserts
  • no-discard-guaranteed invocation flow (not part of trait)
    • Best flow is not the same as nodiscard flow F(args...) vs nodiscard F()

[srl] visit-read: a visitor for serialized values

[srl] visit-read: a visitor for serialized values

Internally use a compile-time id-to-lambda mapping

Based on the experience of developing gcl::io::serialization,
Create a compile-time mapping from cx-type-(id|name|hash) to (<some lambda> | function type-erasure) - yet accessible using runtime index

Motivation

Retrieve T type information from a runtime index/name/hash,
in order to then dispatch it to a generic/overloaded callable (like [](auto && value){ /*decltype(value) is T&&*/ })

POC

[srl] move to stand-alone repository

[srl] move to stand-alone repository

Depends on #45

Motivation

Ease of use ?

Details

csl::srl -> thot-io

Process

Either :

  • Fork, keep only csl::srl component
    or
  • Create from CppShelf as template

[wf] parallelism : syntactic sugar

csl::wf (workflow) : Parallelism : syntactic sugar

parallelization (nice-to-have, requires more design, lifetime management, etc.)

Requires extension for concurrency

auto f1_and_f2 = f1 || f2;

// design 1 : synchronized join
auto route = f1_and_f2 >>= f3;
route(); // csl::wf::invoke(f3, std::future::when_all(f1_future, f2_future));

// design 2 :
auto route = f1_and_f2 |= f3;
route(); // csl::wf::invoke(f3, std::future::when_any(f1_future, f2_future)); 

[ag] format/to_string : compact vs. pretty

[ag] format/to_string : compact vs. pretty

An option should be add to provide a way to differ compact from pretty print (with indentation).

fmt default formatter :

  • (std-)arrays : [ 'a', 'b', 'c' ]
  • tuple : ( 'a', 'b', 'c' )

csl::ag formatter :

  • aggregate : { 'a', 'b', 'c' }

fmt/std::format

Formatter's parse() presentation : 'c' (compact, default), 'p' (pretty)

  • Q : How to propagate depth for json-like indentation ?
  • Q : How to use fmt::join correctly ? With user-defined types ?

Draft/WIP :

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.