ericniebler / meta Goto Github PK
View Code? Open in Web Editor NEWA tiny metaprogramming library
License: Boost Software License 1.0
A tiny metaprogramming library
License: Boost Software License 1.0
... which has divergent variations in meta
, range-v3
, and cmcstl2
.
I suggest the following handy plural form of find_index
:
namespace details {
template<typename List, std::size_t I>
struct find_ind_state {
using L = List;
static const std::size_t Idx = I;
};
template<typename Type>
struct find_indices {
template<typename State, typename T>
using invoke = find_ind_state<
meta::if_< std::is_same<Type,T> ,
meta::push_back< typename State::L, meta::size_t<State::Idx> >,
typename State::L
>,
State::Idx+1
>;
};
};
/** Returns a list of all indices of types equal to \p T in list \p */
template<typename T, typename List>
using find_indices = typename meta::accumulate< List,
details::find_ind_state< meta::list<> , 0 >,
/* accumulate the index list and the current index of the element*/
details::find_indices<T>
>::L;
using ListOfLists = meta::list< meta::list, meta::list<std::string, int>, meta::list<unsigned, long> >;
using tp_t = meta::transpose;
when the above code get compiled, the following errors are generated.
/Users/Michael_Z/open_source/meta/include/meta/meta.hpp:140:9: error: no type named 'type' in 'meta::v1::detail::fold_<meta::v1::list<meta::v1::list<char>, meta::v1::list<std::__1::basic_string<char>, int>, meta::v1::list<unsigned int, long> >, meta::v1::id<meta::v1::list<meta::v1::list<> > >, meta::v1::bind_back<meta::v1::quote<transform>, meta::v1::quote<push_back> > >' using _t = typename T::type; ^~~~~ /Users/Michael_Z/open_source/meta/include/meta/meta.hpp:1303:9: note: in instantiation of template type alias '_t' requested here using fold = _t<detail::fold_<List, id<State>, Fun>>; ^ /Users/Michael_Z/open_source/meta/include/meta/meta.hpp:2452:9: note: in instantiation of template type alias 'fold' requested here using transpose = fold<ListOfLists, ^ test_meta.cpp:12:22: note: in instantiation of template type alias 'transpose' requested here using tp_t = meta::transpose<listOfLists>; ^ 1 error generated.
anyone can figure it out ? thanks.
I was expecting this to succeed. It didn't. Think about it.
static_assert(std::is_same<meta::_t<meta::defer_trait<std::decay, int>>, int>::value, "");
I needed a runtime visitor analogue to meta::for_each
:
using List = meta::list<int,double,float,double>;
std::size_t index = 2
meta::visit<List>(2, [](auto type){ std::cout << "called with type==double"; });
I came up with this, which builds a static function pointer map:
namespace meta
{
namespace details
{
template<typename Func, typename Type>
struct Wrapper
{
static void invoke(Func f) { f(Type{}); }
};
template<typename Func, typename List> struct visit;
template<typename Func, typename... Types>
struct visit<Func, meta::list<Types...>>
{
static constexpr auto makeMap()
{
using FuncPtr = decltype(&Wrapper<Func, void>::invoke);
using Array = std::array<FuncPtr, sizeof...(Types)>;
// Build member function pointers.
return Array{&Wrapper<Func, Types>::invoke...};
}
};
} // namespace details
//! Apply the function `Func` with the type
//! in the `List` at position `index`.
template<typename List, typename Func>
void visit(std::size_t index, Func&& f)
{
using F = decltype(std::forward<Func>(f)); // Define either an lvalue, or rvalue;
constexpr auto map = details::visit<F, List>::makeMap();
if(index >= map.size()){ throw std::out_of_range();}
map[index](std::forward<Func>(f));
}
} // namespace meta
Works like a charm for example with an rvalue lambda:
MY_TEST(MetaProgramming, Test1)
{
using List = meta::list<int, double, short>;
int b = -1;
meta::visit<List>(2,
[&](auto type) {
using T = decltype(type);
if constexpr(std::is_same<T, short>{})
{
b = 2;
}
});
ASSERT_EQ(b, 2) << "Meta Visit failed!";
}
The docs talk about metafunctions first and foremost, but Meta isn't fundamentally about metafunctions. Rethink how we present the core concepts of Meta. And we really need to discuss let
, lambda
, and the lazy::
namespace.
I know it's a long shot, since I see now newer versions use inline namespaces and constexpr, and I appreciate the desire to keep this lightweight, but it would be tremendously useful to be able to use this in VC2013 (which lacks at least those two things)
Is there any way to utilize this on MSVC
?
I'm getting compiler errors when I try.
#include <meta/meta.hpp>
struct is_positive {
template<typename N>
using invoke = meta::bool_<(N::type::value > 0)>;
};
using list = meta::list<
meta::int_<1>,
meta::int_<2>,
meta::int_<3>,
meta::int_<4>
>;
using result = meta::all_of<list, is_positive>;
...results in:
In file included from /Users/eniebler/Code/range-v3/scratch/scratch.cpp:5:
/Users/eniebler/Code/range-v3/include/meta/meta.hpp:560:9: error: pack expansion used as argument for non-pack parameter of alias template
using invoke = typename F::template invoke<Args...>;
^~~~~
FAILED: meta/test/CMakeFiles/test.meta.dir/meta.cpp.o
/usr/bin/clang++ -I/mnt/c/Users/Вова/projects/cpp-project-template/meta/include -std=c++11 -ftemplate-backtrace-limit=0 -Weverything -Werror -pedantic-errors -Wdocumentation -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-old-style-cast -Wno-documentation-unknown-command -Wno-missing-prototypes -O2 -g -DNDEBUG -fcolor-diagnostics -MD -MT meta/test/CMakeFiles/test.meta.dir/meta.cpp.o -MF meta/test/CMakeFiles/test.meta.dir/meta.cpp.o.d -o meta/test/CMakeFiles/test.meta.dir/meta.cpp.o -c '/mnt/c/Users/Вова/projects/cpp-project-template/meta/test/meta.cpp'
/mnt/c/Users/Вова/projects/cpp-project-template/meta/test/meta.cpp:1966:45: error: result of '10 ^ 15' is 5; did you mean '1e15'? [-Werror,-Wxor-used-as-pow]
static_assert(std::is_same<int_<(10 ^ 15)>, bit_xor<int_<10>, int_<15>>>::value, "");
~~~^~~~
1e15
/mnt/c/Users/Вова/projects/cpp-project-template/meta/test/meta.cpp:1966:45: note: replace expression with '0xA ^ 15' or use 'xor' instead of '^' to silence this warning
/mnt/c/Users/Вова/projects/cpp-project-template/meta/test/meta.cpp:2101:39: error: result of '10 ^ 15' is 5; did you mean '1e15'? [-Werror,-Wxor-used-as-pow]
std::is_same<int_<(10 ^ 15)>, let<lazy::bit_xor<int_<10>, int_<15>>>>::value, "");
~~~^~~~
1e15
/mnt/c/Users/Вова/projects/cpp-project-template/meta/test/meta.cpp:2101:39: note: replace expression with '0xA ^ 15' or use 'xor' instead of '^' to silence this warning
2 errors generated.
id<T>
: A trait that always returns its argument T
. Also, a Callable that always returns T
.
Other libraries use always<T>
or const_<T>
for a Callable that always returns T
.
Naming id<T>
a Callable that doesn't behaves as the identify Callable is disturbing. I believe that id<T>
should only be a trait and that we could add an meta::identity
Callable
struct identity {
template < class T>
using invoke = T;
};
it can also be defined as
using identity = quote_trait<id>;
What do you think?
Is there any way to support early abortion in meta::for_each
(or the like).
So basically to do something at runtime for a specific Trait
we can do:
int index = 2;
using A = meta::list<TraitA, TraitB , TraitC>;
meta::for_each(A{}, [](auto a){
using Trait = decltype(a);
if (meta::find_index<A, Trait> == index)
{
// do something ...
return false; // abort iteration, not possible right now
}
return true; // true for continuation, not possible right now
});
So far since its a aggregate initialization under the hood, this will not be possible.
Is there a better way of doing the above? With something else?
I've long been bothered by meta's use of eval
to do something as trivial as typename T::type
. I would rather use eval
to evaluate deferred expressions (currently achieved by doing a let
without any argument bindings, which reads a bit awkwardly IMO). Besides, since meta doesn't trade in metafunctions the way MPL does, it's wrong to encourage users to think of accessing a nested type as "evaluating" anything.
Until recently, I couldn't think of a better name for the thing eval
does. But how about this:
template <typename T>
using _t = typename T::type;
template <typename T>
constexpr typename T::type::value_type _v = T::type::value;
This introduces _t
as fetching a nested ::type
and _v
as fetching a nested ::value
. These are obviously intended to evoke the xxx_t
and xxx_v
traits and variable templates in <type_traits>
.
@gnzlbg, thoughts?
This change [2e1f098] caused about a 20% compile time perf hit for range-v3. Think about alternatives.
Metabench is timing out because of it: https://travis-ci.org/ldionne/metabench/jobs/294051656#L1991
Also, graphs agree with this: http://metaben.ch/type/clang++-4.0/partition/index.html
It's fine if there's no desire to make it faster, but I'll reduce its range from Metabench to avoid timing out. Just let me know.
The code below gives meta::lambda
variadic placeholders, but it's heavier weight than the existing lambda implementation. Find a way to make this lighter, or else provide both implementations and switch when a variadic placeholder is used.
template<typename T>
struct is_vararg
: std::false_type
{};
template<typename T>
struct is_vararg<vararg<T>>
: std::true_type
{};
template<typename A, typename T, typename F, typename Ts>
struct subst1_
: id<list<list<T>>>
{};
template<typename T, typename F, typename Ts>
struct subst1_<F, T, F, Ts>
: id<list<>>
{};
template<typename A, typename T, typename F, typename Ts>
struct subst1_<vararg<A>, T, F, Ts>
: id<list<eval<Ts>>>
{};
template<typename As, typename Ts>
using substitutions_ =
push_back<join<transform<
concat<As, repeat_n_c<size<Ts>{} + 2 - size<As>{}, back<As>>>,
concat<Ts, list<back<As>, back<As>>>,
compose<quote<eval>,
bind_back<
quote<subst1_>,
back<As>,
lazy::drop<Ts, minus<size<As>, meta::size_t<2>>>>>>>, list<back<As>>>;
template<typename As>
using is_variadic = is_vararg<at<push_front<As, void>, dec<size<As>>>>;
template<typename As, typename Ts>
using substitutions =
eval<if_c<((is_variadic<As>::value && size<Ts>::value + 2 >= size<As>::value) ||
(size<Ts>::value + 1 == size<As>::value)), defer<substitutions_, As, Ts>>>;
template<int, typename...As>
struct lambda2_
{
private:
template<int N, typename...Us> friend struct lambda2_;
using Tags = list<As...>; // Includes the lambda body as the last arg!
template<typename T, typename Args> struct impl;
template<typename Args>
using eval_impl = compose<quote<eval>, bind_back<quote<impl>, Args>>;
template<template<typename...> class C, typename Args, typename Ts>
using subst_ = apply_list<quote<C>, join<transform<Ts, eval_impl<Args>>>>;
template<typename, typename, typename = void>
struct impl_
{};
template<template<typename...> class C, typename...Ts, typename Args>
struct impl_<defer<C, Ts...>, Args, void_<subst_<C, Args, list<Ts...>>>>
{
using type = list<subst_<C, Args, list<Ts...>>>;
};
template<typename T, typename Args>
struct impl
: if_<in<Tags, T>, lazy::at<Args, reverse_find_index<Tags, T>>, id<list<T>>>
{};
template<template<typename...> class C, typename...Ts, typename Args>
struct impl<defer<C, Ts...>, Args> : impl_<defer<C, Ts...>, Args>
{};
template<template<typename...> class C, typename...Ts, typename Args>
struct impl<C<Ts...>, Args> : impl_<defer<C, Ts...>, Args>
{};
template<int N, typename...Bs, typename Args>
struct impl<lambda2_<N, Bs...>, Args>
{
using type = list<compose<
typename lambda2_<0, As..., Bs...>::thunk,
bind_front<quote<concat>, Args>,
curry<bind_front<quote<substitutions>, list<Bs...>>>>>;
};
struct thunk
{
template<typename S, typename R = eval<impl<back<Tags>, S>>>
using apply = if_c<size<R>{} == 1, front<R>>;
};
public:
template<typename...Ts>
using apply = meta::apply<thunk, substitutions<Tags, list<Ts...>>>;
};
template<typename...As>
using lambda2 = lambda2_<0, As...>;
meta
's test coverage is somewhat limited (although @kedarbhat is working on adding more). range-v3 and cmcstl2 are the real
meta test suite; we should incorporate them into the continuous integration testing.
Suggestion: Rename list_element
to at
.
The metafunction list_element
is the equivalent of at
in std, such as std::vector
.
list_element
also contains the name of the metatype (i.e. list
), which none of the other metafunctions in meta do (well except for as_list
, but that doesn't count... ;-) ). I guess that this is due to simply element
being a bit vague on its own.
To summarize at
seems to be short, descriptive and following the std
nomenclature.
There are issues in the interaction between the concept constraints in meta and the lambda implementation that need to be investigated. Concepts are applied when forming a template-id, even before instantiation, so e.g.:
template<class> concept Foo = false;
template<Foo T> struct X;
using U = X<int>; // ill-formed despite not instantiating X<int>
Yet the implementation of lambdas relies on the ability to name "invalid" specializations which are later pattern-matched and substituted into valid specializations. GCC's concept implementation is lax enough that we've been getting by with some cheating in cmcstl2, but clang-concepts (and I suspect other implementations of C++20 concepts) refuses to compile the lambda test cases in meta.cpp.
Hi,
I need a type traits to check if a Callable Fn
can be invoked with some specific parameters Args...
and if the result is convertible to a type R
. Something in line with std::is_callable
but using meta::invoke
instead of std::invoke
.
As meta::is_callable
is already used what would be the name of this new type trait?
I would prefer to rename the current meta::is_callable
to something else ut I don't have a good proposal. I use in my own code is_type_constructor
as Meta-Callables are are functions that construct types.
template <class, R = void>
struct is_callable; // not defined
template <class Fn, class ...Args, class R>
struct is_callable<Fn(Args...), R>;
template <class Sig, R = void>
constexpr bool is_callable_v = is_callable <Sig, R>::value;
If TC::template invoke<Xs...>
is well formed then
if R
is void
then true_type
else std::is_convertible<meta::invoke_t<Xs...>, R>
else false_type
.
No conversion check is performed when R
is void
.
BTW, in order to avoid confusion between std Callables and meta Callables what do you think to rename Callable to Meta-Callable?
Cmake works fine..
but during compilation:
/home/robotserver/meta/example/tutorial_snippets.cpp:268:5: error: static assertion failed:
static_assert(std::is_same<meta::invoke<t, unsigned>, int const &>{}, "");
Seems to be related to the biicode part of the travis.yml file in after_success
. I guess it doesn't like being called by multiple jobs on each build. @Manu343726 could you take a look?
namespace meta_detail
{
template<typename Ts, int = 0>
struct lambda_rec_;
template<typename ...Ts>
struct lambda_rec_<list<Ts...>>
{
template<typename...Us>
using apply = apply<let<var<_self, lambda_rec_>, lambda<Ts...>>, Us...>;
};
}
////////////////////////////////////////////////////////////////////////////////////
// lambda_rec
/// For defining a self-recursive lambda. In the body of a lambda, the placeholder
/// `_self` refers to the nearest enclosing `lambda_rec`
///
/// \code
/// // Define a factorial template in terms of a recursive lambda
/// using factorial_ = lambda_rec<_a,
/// lazy::if_<lazy::greater<_a, meta::size_t<0>>,
/// lazy::multiplies<_a, lazy::apply<_self, lazy::dec<_a>>>,
/// meta::size_t<1>>>;
/// template<std::size_t N>
/// using factorial = apply<factorial_, meta::size_t<N>>;
///
/// static_assert(factorial<0>::value == 1, "");
/// static_assert(factorial<1>::value == 1, "");
/// static_assert(factorial<2>::value == 2, "");
/// static_assert(factorial<3>::value == 6, "");
/// static_assert(factorial<4>::value == 24, "");
/// \endcode
template<typename ...Ts>
using lambda_rec = meta_detail::lambda_rec_<list<Ts...>>;
What are the benefits of using meta
instead of boost::hana
?
I think boost::hana
can do the same and much more (glue between compile-time and run-time), and meta ist partially a nice small meta programming library (which only supports compile-time computing, except for meta::for_each) from which everything can be built on top?.
Is there some concense about that? And what do you recommend?
I am wondering why the implementation of for_each
uses the CTOR in f(Args{})
.
The following constructs the HugeObj
just to run the lambda:
struct HugeObj
{
HugeObj(){ std::cout << "Huge Obj CTOR";}
};
meta::for_each(meta::list<HugeObj>{}, [](auto&&r){});
Wouldnt it better to have a pointer
meta::for_each(meta::list<HugeObj>{}, [](auto*p){});
or a Functor f
with a templated invoke<T>
function
where the implementation would look like this:
return (void)std::initializer_list<int>{((void)f.invoke<Args>()), 0)...}, f;
The link for 'A tiny metaprogramming library' generates 'Error establishing a database connection'.
When using meta and curses together, a name collision occurs, as there is a function called meta in curses, see http://linux.die.net/man/3/meta
When compiling an empty program like this
#include <meta/meta.hpp>
int main() {}
The following compilation errors come from the header file:
FAILED: CMakeFiles/cpp-project-template.dir/main.cpp.o
/usr/bin/c++ -I/mnt/c/Users/Вова/projects/cpp-project-template/meta/include -O2 -g -DNDEBUG -fdiagnostics-color=always -Wall -Wextra -Wpedantic -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused -Woverloaded-virtual -Wconversion -Wsign-conversion -Wnull-dereference -Wdouble-promotion -Wformat=2 -Werror -Wmisleading-indentation -Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wuseless-cast -std=gnu++2a -MD -MT CMakeFiles/cpp-project-template.dir/main.cpp.o -MF CMakeFiles/cpp-project-template.dir/main.cpp.o.d -o CMakeFiles/cpp-project-template.dir/main.cpp.o -c '/mnt/c/Users/Вова/projects/cpp-project-template/main.cpp'
In file included from /mnt/c/Users/Вова/projects/cpp-project-template/main.cpp:1:
/mnt/c/Users/Вова/projects/cpp-project-template/meta/include/meta/meta.hpp:2657:53: error: use of old-style cast to ‘std::size_t’ {aka ‘long unsigned int’} [-Werror=old-style-cast]
2657 | using type = meta::size_t<((std::size_t)_v<std::is_same<T, Ts>> + ...)>;
| ^~~~~~~~~~~~~~~~~~~~~~~
/mnt/c/Users/Вова/projects/cpp-project-template/meta/include/meta/meta.hpp:2733:80: error: use of old-style cast to ‘bool’ [-Werror=old-style-cast]
2733 | using type = meta::size_t<((std::size_t)(bool)invoke<Fn, L>::type::value + ...)>;
| ^~~~~
/mnt/c/Users/Вова/projects/cpp-project-template/meta/include/meta/meta.hpp:2733:80: error: use of old-style cast to ‘std::size_t’ {aka ‘long unsigned int’} [-Werror=old-style-cast]
In ericniebler/range-v3@970ce2e, I added meta::count
, meta::count_if
, and meta::let
. These need to be merged.
The hard-error if one of the types passed in is not a Boolean Integral.
Hi I am wondering if the interface of transform is wrong?
According to:
Lines 1522 to 1523 in 9780f35
it can only be called with arguments. But where is the fun without a Fun
template?
And second, why does transform not expect a list of something like all the other meta algorithms but an unpacked list?
Right now, I am resorting to the detail implementation which seems to be the correct one.
I get a compile error with clang 10.0:
meta/meta_fwd.hpp:286:20: error: expected concept name with optional arguments
[build] { T{} } -> typename T::value_type;
[build] ^
Whats the fix?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.