Giter Site home page Giter Site logo

juanchristensen / mp Goto Github PK

View Code? Open in Web Editor NEW

This project forked from boost-ext/mp

0.0 0.0 0.0 277 KB

MP: C++20 ~~Template~~ Meta-Programming

Home Page: https://boost-ext.github.io/mp

License: Boost Software License 1.0

C++ 2.79% HTML 96.69% CMake 0.47% Dockerfile 0.04%

mp's Introduction

Linux Try it online

MP - Template Meta-Programming

| Motivation | Quick Start | Overview | Tutorial | Examples | User Guide | Benchmarks | FAQ |

C++ single header/single module C++20 Meta-Programming Library

Motivation

Make Template Meta-Programming easier by leveraging run-time approach at compile-time. If one knows how to use stl.algorithms/ranges one can consider themself a TMP expert now as well!

#include <ranges>

auto hello_world = [](auto list, auto add_const, auto has_value){
  return list                          // int, foo, val, bar
   | std::views::drop(1_c)             // foo, val, bar
   | std::views::reverse               // bar, val, foo
   | std::views::take(2_c)             // bar, val
   | std::views::transform(add_const)  // bar const, val const
   | std::views::filter(has_value)     // val const
   ;
};
auto add_const = []<class T> -> T const {};
auto has_value = []<class T> { return requires(T t) { t.value; }; };

struct bar {};
struct foo { int value; };
struct val { int value; };
static_assert(mp::list<val const> ==
  hello_world(mp::list<int, foo, val, bar>, add_const, has_value)
);
int main () {
  struct stub_type{
    std::size_t type{};
    bool has_value{};
    bool add_const{};
    constexpr auto operator<=>(const stub_type&) const = default;
  };

  "hello world"_test = [] {
    // given
    std::vector list{stub_type{.type = 0},
                     stub_type{.type = 1, .has_value = true}};

    auto add_const = [](auto& t) { t.add_const = true; return t; };
    auto has_value = [](auto t)  { return t.has_value; };

    // when
    auto out = hello_world(list, add_const, has_value);
    const std::vector<stub_type> result{std::begin(out), std::end(out)};

    // then
    expect(1_u == std::size(result));
    expect(result[0] == stub_type{.type = 1,
                                  .has_value = true,
                                  .add_const = true});
  };
}

https://godbolt.org/z/3T86zvcEn


// write once, use multiple times
auto slice = [](auto list, auto begin, auto end) {
  return list
    | std::views::drop(begin)
    | std::views::take(end - 1_c)
    ;
};
// type_list
static_assert(slice(mp::list<int, double, float, short>, 1_c, 3_c) ==
                    mp::list<double, float>);
// variant.type
static_assert(std::is_same_v<
    mp::typeof<slice, std::variant<int, double, float, short>, 1_c, 3_c>,
                      std::variant<double, float>>
);
// value_list
static_assert(slice(mp::list<1, 2, 3, 4>, 1_c, 3_c) ==
                    mp::list<2, 3>);
// fixed_string
static_assert(slice(mp::list<"foobar">, 1_c, 3_c) ==
                    mp::list<"oo">);
// tuple of values
static_assert(slice(std::tuple{1, 2, 3, 4}, 1_c, 3_c) ==
                    std::tuple{2, 3});
#include <cassert>

int main(int argc, const char**) {
  // run-time tuple of values
  assert((slice(std::tuple{1, argc, 3, 4}, 1_c, 3_c) ==
                std::tuple{argc, 3}));
}

https://godbolt.org/z/ePE9aqYTe


auto fun_with_tuple = [](auto tuple) {
  return tuple
    | std::views::filter([](auto i) -> bool { return i % 2; })
    | std::views::reverse
    | std::views::drop(1_c)
    ;
};

static_assert(std::tuple{5, 3, 1} == fn([] { return std::tuple{1, 2, 3, 4, 5, 6, 7}; }));

#include <algorithm>

auto sort_by_size = [](std::ranges::range auto types) {
  std::ranges::sort(types, [](auto lhs, auto rhs) { return lhs.size < rhs.size; });
  return types;
};

/**
 * Verify/debug at run-time
 */
int main () {
  "sort by size"_test = [] {
    // given
    const auto m1 = meta{.index = 0, .size = 2};
    const auto m2 = meta{.index = 1, .size = 1};
    const auto m3 = meta{.index = 2, .size = 3};

    // when
    const auto sorted = sort_by_size({m1, m2, m3});

    // then
    expect({m2, m1, m3} == sorted);
  };
}
struct not_packed {
  char c{};
  int i{};
  std::byte b{};
};

/**
 * Check at compile-time
 */
static_assert(sizeof(not_packed) == 12u);
static_assert(sizeof(to_tuple(not_packed{}) | sort_by_size) == 8u);

#include <ranges>
#include <algorithm>

auto rotate = [](std::ranges::range auto types) {
    std::ranges::rotate(types, std::begin(types) + 1);
    return types;
};

static_assert((boost::mp::list<int, double, float>() | rotate) ==
               boost::mp::list<double, float, int>());

Quick Start

Try it out - https://godbolt.org/z/ePE9aqYTe


Locally

docker build . -t dev # or docker pull krisjusiak/dev
docker run -it -v "$(pwd)":/mp --privileged dev:latest
mkdir build && cd build
CXX={clan}g++ cmake .. -DBOOST_MP_BUILD_TESTS=ON -DBOOST_MP_BUILD_EXAMPLES=ON
cmake --build . -j
ctest --output-on-failure

Overview

  • Single C++20 header/module
  • Minimal learning curve (reuses STL, ranges or any third-party algorithms for stl.container)
  • Easy debugging (meta-functions can be simply run at run-time!)
  • Same interface for types/values/tuples
  • Declarative by design (composable using pipe operator, support for ranges)
  • Fast compilation times (see benchmarks)

Requirements (Dockerfile)

  • C++20 compliant compiler (STL with support for constexpr std::vector)
    • clang++15+ [libc++-15+] (✔️)
    • g++12+ [libstdc++-12+] (✔️)

Tutorial

Firstly include or import boost.mp

#include <boost/mp.hpp>

or

import boost.mp;

Okay, let's write a hello world, shall we?

First step is to add our new meta-function.

auto identity = [](std::ranges::range types) {
  return types;
};

meta is a meta objects range (like vector<meta>) which we can do operations on. For example, sorting, changing the size, removing elements, etc...

Let's apply our first meta-function.

auto magic = mp::list<int, double, float>() | identity;
static_assert(magic == mp::list<int, double, float>());

Yay, we have the first meta-function done. Notice the pipe (|) operator. By using it multiple meta-functions can be combined together.

For the next example including/importing ranges will be required

#include <ranges>

Let's implement simple slice for types as an example

template<auto list, auto Start, auto End>
auto slice = list
   | std::views::drop(Start)
   | std::views::take(End);

Notice that we've just used std::ranges at compile-time to manipulate a type-list!

using mp::operator""_c;
static_assert(slice<mp::list<int, double, float>(), 1_c, 2_c>
           == mp::list<double, float>());

""_c is an User Defined Literal which represents constant integral value which is required for simulating passing constexpr parameters which aren't supported in C++.

Let's add STL too, why not

#include <tuple>
#include <algorithm>

This time we will sort and reverse a tuple

Note: All operations are supported for the following entities

  • mp::type_list
  • mp::value_list
  • mp::fixed_string
  • std::tuple

Additionally type_list/value_list/fixed_string will be deduced automatically based on parameters when mp::list<...>() is used.

Okay, coming back to our sort...

template <auto Fn>
auto sort = [](std::ranges::range auto types) {
  std::sort(std::begin(types), std::end(types), Fn);
  return types;
};

> Note With ranges that could be `actions::sort(types, Fn)`

auto by_size = [](auto lhs, auto rhs) { return lhs.size < rhs.size; };

So far, nothing magical, same code as in run-time!

Let's apply it then

using mp::operator|;
auto pack = [](auto t) {
  return mp::to_tuple(t) | sort<by_size>;
}

Note: We used to_tuple which converts a struct into a tuple using reflection. There is also to_list available which produces type_list.

As usual, we use pipe (|) to compose functionality.

struct not_packed {
  char c{};       // 1b
  int i{};        // 4b
  std::byte b{};  // 1b
};
static_assert(sizeof(not_packed) == 12u);
static_assert(sizeof(pack(not_packed{})) == 8u);

Okay, so far so good, but what about adding or removing from type_list?

Removing is simple as we can just erase elements from the meta types as before.

Dealing with new types it's a bit different but not difficult either.

template <auto List, class... Ts>
auto add = List | mp::list<Ts...>();
static_assert(add<mp::list<int, double>(), void> ==
                  mp::list<int, double, void>());

And what about transform? Let's add pointers to all our type in the list.

auto transform = [](auto list){
  return list | std::views::transform([]<class T> -> Ts* const {})
};

It's that easy, we just apply addition of const pointer to all Ts....

static_assert(transform(mp::list<int, double>) ==
                        mp::list<int* const, double* const>);

Okay, so what about the case when we need meta-types and Ts...?

auto filter = std::views::filterclass T> { return requires(T t) { t.value; }; });

Notice handy requires with lambda pattern to verify ad-hoc concepts.

struct bar {};
struct foo {
  int value;
};
static_assert(mp::list<foo>() ==
             (mp::list<foo, bar>() | filter));

That's it for now, for more let's take a look at more Examples in the following section and the User-Guide.

Examples

User-Guide

/**
 * Library version for example 1'0'0
 */
#define BOOST_MP_VERSION
/**
 * Forces using includes even if modules are supported
 */
#define BOOST_MP_DISABLE_MODULE
/**
 * A meta type representation to be manipulated
 * vector<meta> is passed to pipe lambdas
 */
struct meta {
  std::size_t pos{};
  std::size_t size{};
  //...
}
/**
 * Returns unique integral (std::size_t) representation of type
 * Should only be used for comparison
 * static_assert(type_id<void> ! = type_id<int>);
 */
template <class T> constexpr auto type_id;
/**
 * Returns type/value name as string_view
 * static_assert(type_name<void>() == "void");
 * static_assert(type_name<42>() == "42");
 */
template <template auto T> [[nodiscard]] constexpr auto type_name()
/**
 * A meta concept which verifies meta list
 * static_assert(concepts::meta<list<>>>);
 * static_assert(concepts::meta<list<int, double>>>);
 * static_assert(concepts::meta<std::tuple<int, double>>>);
 */
concept concepts::meta;
/**
 * List of types
 */
template<class... Ts> struct type_list {
  constexpr auto size = sizeof...(Ts);
  constexpr operator==(type_list) = default;
  constexpr operator[](auto N); // returns N-th type
};
/**
 * List of values
 */
template<class... Ts> struct value_list;
  constexpr auto size = sizeof...(Vs);
  constexpr operator==(value_list) = default;
  constexpr operator[](auto N); // returns N-th value
};
/**
 * Compile-time string representation to be used
 * as <"Hello World">
 */
template<std::size_t N> struct fixed_string {
  static constexpr auto size = N;
  [[nodiscard]] constexpr auto operator<=>(const fixed_string&) const = default;
  [[nodiscard]] constexpr explicit(false) operator std::string_view() const;
  std::array<char, N + 1> data{};
};
/**
 * Deduces correct list based on types
 *  type_list for Ts...
 *  value_list for auto...
 *  fixed_string for if { t.data; t.size; }
 */
template<template auto... Vs> [[nodiscard]] constexpr auto list();
/**
 * Converts type into a type_list by reflecting fields
 * 0-10 number of reflected fields is supported
 * @tparam T type to be reflected
 */
template<class T> [[nodiscard]] constexpr auto to_list;
/**
 * Converts type into a std::tuple by reflecting fields
 * 0-10 number of reflected fields is supported
 * @param obj object to be reflected
 */
constexpr auto to_tuple = []<class T>(T&& obj);
/**
 * Composability pipe operator for types
 * @param fn functor to be applied
 * - [](concepts::meta auto types)
 * - []<class... Ts>
 * - []<class... Ts>(concepts::meta auto types)
 */
template <template <class...> class T, class... Ts>
[[nodiscard]] constexpr auto operator|(T<Ts...>, auto fn);
/**
 * Composability pipe operator for values
 * @param fn functor to be applied
 * - [](concepts::meta auto types)
 * - []<auto... Ts>
 * - []<auto... Ts>(concepts::meta auto types)
 */
template <template <auto...> class T, auto... Vs>
[[nodiscard]] constexpr auto operator|(T<Vs...>, auto fn);
/**
 * Composability pipe operator for std::tuple
 * @param fn functor to be applied
 * - [](concepts::meta auto types)
 * - [](auto&&... args)
 * - [](concepts::meta auto types, auto&&... args)
 */
template <template <class...> class T, class... Ts>
[[nodiscard]] constexpr auto operator|(std::tuple<Ts...>, auto fn);
/**
 * Compile time integral value representation (required to mimic constexpr parameters)
 * static_assert(42 == _c(1+41));
 * static_assert(42 == 42_c);
 */
template <auto N> constexpr auto _c;
template <char... Cs> [[nodiscard]] consteval auto operator""_c();

Benchmarks

To build/run benchmarks

cd benchmark
mkdir build && cd build
CXX={clang}g++ cmake ..
make <<benchmark>>

FAQ

CONTRIBUTING


Disclaimer MP is not an official Boost library.

mp's People

Contributors

juanchristensen avatar justend29 avatar kris-jusiak avatar krzysztof-jusiak avatar ldm5180 avatar sehe avatar

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.