From 6d8c294a3ab33e8b0f10025bb0a1b62403d70445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=27Griwes=27=20Dominiak?= Date: Mon, 20 Feb 2017 21:49:53 +0100 Subject: [PATCH 1/3] typeclass: initial macro-based version. Rough TODO for the future: * SFINAEable typeclass instances (even for non-templated typeclasses) * honor cvref qualifiers on member declarations * allow overloads of member functions (possibly with tpl::vector) * add checking for instance exhaustiveness, i.e. whether all functions are implemented (not 100% sure how to do this) * support typeclass hierarchies * allocation policies for `erased` * possibly "vtable policies" like Louis Dionne's `te`'s? * reflection-powered version of the library (this one requires a working reflection implementation (duh)) * generation of language concepts for the typeclasses --- include/reaver/function_traits.h | 112 ++++++++ include/reaver/typeclass/typeclass.h | 384 +++++++++++++++++++++++++++ tests/typeclass/typeclass.cpp | 189 +++++++++++++ 3 files changed, 685 insertions(+) create mode 100644 include/reaver/function_traits.h create mode 100644 include/reaver/typeclass/typeclass.h create mode 100644 tests/typeclass/typeclass.cpp diff --git a/include/reaver/function_traits.h b/include/reaver/function_traits.h new file mode 100644 index 0000000..22d539f --- /dev/null +++ b/include/reaver/function_traits.h @@ -0,0 +1,112 @@ +/** + * Reaver Library Licence + * + * Copyright © 2017 Michał "Griwes" Dominiak + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation is required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + **/ + +#pragma once + +namespace reaver +{ +inline namespace _v1 +{ + enum class ref_qualifier + { + none, + lvalue, + rvalue + }; + + template + struct function_traits; + + template + struct function_traits + { + using return_type = Ret; + constexpr static bool is_const = IsConst; + constexpr static bool is_volatile = IsVolatile; + constexpr static ref_qualifier reference_qualifier = RefQualifier; + + template typename Template, typename... AdditionalArguments> + using explode = Template; + }; + + template + struct function_traits : function_traits + { + }; + + template + struct function_traits : function_traits + { + }; + + template + struct function_traits : function_traits + { + }; + + template + struct function_traits : function_traits + { + }; + + template + struct function_traits : function_traits + { + }; + + template + struct function_traits : function_traits + { + }; + + template + struct function_traits : function_traits + { + }; + + template + struct function_traits : function_traits + { + }; + + template + struct function_traits : function_traits + { + }; + + template + struct function_traits : function_traits + { + }; + + template + struct function_traits : function_traits + { + }; + + template + using return_type = typename function_traits::return_type; + + template typename Template, typename... AdditionalArguments> + using explode = typename function_traits::template explode; +} +} diff --git a/include/reaver/typeclass/typeclass.h b/include/reaver/typeclass/typeclass.h new file mode 100644 index 0000000..1ab9b04 --- /dev/null +++ b/include/reaver/typeclass/typeclass.h @@ -0,0 +1,384 @@ +/** + * Reaver Library Licence + * + * Copyright © 2017 Michał "Griwes" Dominiak + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation is required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + **/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include "../function_traits.h" + +namespace reaver +{ +inline namespace _v1 +{ + template + struct typeclass_trait; + + template + struct typeclass_trait::value>::type> + { + using type = typename T::template instance; + }; + + template + struct typeclass_trait::value>::type> + { + using type = typename Typeclass::template instance; + }; + + template + using tc_instance = typename typeclass_trait::type; + +#define TYPECLASS_INTERNAL_REAVER_NAMESPACE ::reaver + +#define INSTANCE_TEMPLATE_HELPER \ + template \ + struct typeclass_instance_helper + +#define TYPECLASS_INSTANCE(...) \ + template \ + struct instance + +#define TYPECLASS_INSTANCE_TEMPLATE(template_decl, template_args, class) \ + template \ + struct instance : typeclass_instance_helper<_typeclass, ONLY template_args> \ + { \ + } + +#define DEFAULT_INSTANCE(typeclass, ...) \ + template \ + struct typeclass::instance + +#define DEFAULT_INSTANCE_TEMPLATE(template_decl, template_args, typeclass, ...) \ + INSTANCE_TEMPLATE_HELPER; \ + template \ + template \ + struct typeclass::instance, __VA_ARGS__> + +#define SPECIAL_INSTANCE(typeclass, class) \ + template<> \ + struct typeclass::instance + +#define INSTANCE(typeclass, class) \ + template<> \ + struct class ::instance : public typeclass::instance + +#define INSTANCE_TEMPLATE(template_decl, template_args, typeclass, class) \ + INSTANCE_TEMPLATE_HELPER; \ + template \ + struct typeclass_instance_helper, class> \ + : public typeclass::template instance, class> + + template + struct virtual_dtor + { + virtual ~virtual_dtor() = default; + }; + + template + struct typeclass_provide_data_member + { + T t = {}; + }; + +#define CONCAT(X, Y) X##Y +#define CONCAT3(X, Y, Z) X##Y##Z + +#define TYPECLASS_PREPARE_MIXINS(x, typeclass_name, memfn_name) \ + template \ + struct CONCAT3(typeclass_name, _typeclass_base_provide_, memfn_name) \ + : protected TYPECLASS_INTERNAL_REAVER_NAMESPACE::virtual_dtor \ + { \ + virtual ReturnType memfn_name(Args...) = 0; \ + }; \ + \ + template \ + struct CONCAT3(typeclass_name, _typeclass_impl_provide_, memfn_name) \ + : virtual CONCAT3(typeclass_name, _typeclass_base_provide_, memfn_name), \ + protected virtual TYPECLASS_INTERNAL_REAVER_NAMESPACE::typeclass_provide_data_member \ + { \ + CONCAT3(typeclass_name, _typeclass_impl_provide_, memfn_name) \ + (const T & t) : TYPECLASS_INTERNAL_REAVER_NAMESPACE::typeclass_provide_data_member{ t } \ + { \ + } \ + \ + virtual ReturnType memfn_name(Args... args) override \ + { \ + return Typeclass::memfn_name(TYPECLASS_INTERNAL_REAVER_NAMESPACE::typeclass_provide_data_member::t, std::forward(args)...); \ + } \ + }; \ + \ + template \ + struct CONCAT3(typeclass_name, _typeclass_provide_, memfn_name) : protected TYPECLASS_INTERNAL_REAVER_NAMESPACE::virtual_dtor \ + { \ + template \ + static ReturnType memfn_name(T & t, Args... args) \ + { \ + return TYPECLASS_INTERNAL_REAVER_NAMESPACE::tc_instance::memfn_name(t, std::forward(args)...); \ + } \ + }; \ + \ + template \ + struct CONCAT3(typeclass_name, _typeclass_erased_provide_, memfn_name) \ + : protected TYPECLASS_INTERNAL_REAVER_NAMESPACE::virtual_dtor, \ + protected virtual TYPECLASS_INTERNAL_REAVER_NAMESPACE::typeclass_provide_data_member> \ + { \ + ReturnType memfn_name(Args... args) \ + { \ + return TYPECLASS_INTERNAL_REAVER_NAMESPACE::typeclass_provide_data_member>::t->memfn_name( \ + std::forward(args)...); \ + } \ + }; \ + \ + template \ + struct CONCAT3(typeclass_name, _typeclass_erased_instance_provide_, memfn_name) \ + { \ + static ReturnType memfn_name(typename Typeclass::erased_ref & t, Args... args) \ + { \ + return t.memfn_name(std::forward(args)...); \ + } \ + }; + + template + struct strip_arguments; + + template typename Template, typename... Args> + struct strip_arguments> + { + template + using type = Template; + }; + +#define NOOP(...) +#define ONLY(...) __VA_ARGS__ +#define FIRST(...) __VA_ARGS__ NOOP +#define SECOND(...) ONLY + +#define TYPECLASS_FRIEND_MIXIN(x, typeclass_name, memfn_name) \ + template \ + friend struct CONCAT3(typeclass_name, _typeclass_erased_provide_, memfn_name); + +#define TYPECLASS_TYPE_BASE_CLASS(x, typeclass_info, memfn_name) TYPECLASS_TYPE_BASE_CLASS_IMPL(FIRST typeclass_info, SECOND typeclass_info, memfn_name) +#define TYPECLASS_TYPE_BASE_CLASS_IMPL(typeclass_name, template_args, memfn_name) \ +private \ + TYPECLASS_INTERNAL_REAVER_NAMESPACE::explode, + +#define TYPECLASS_TYPE_BASE_USING(x, typeclass_info, memfn_name) TYPECLASS_TYPE_BASE_USING_IMPL(FIRST typeclass_info, SECOND typeclass_info, memfn_name) +#define TYPECLASS_TYPE_BASE_USING_IMPL(typeclass_name, template_args, memfn_name) \ + using TYPECLASS_INTERNAL_REAVER_NAMESPACE::explode< \ + typename CONCAT(typeclass_name, _definition) \ + template_args::memfn_name, /*reaver::strip_arguments::template type,*/ \ + CONCAT3(typeclass_name, _typeclass_provide_, memfn_name), \ + typeclass_name template_args>::memfn_name; + +#define TYPECLASS_BASE_BASE_CLASS(x, typeclass_info, memfn_name) TYPECLASS_BASE_BASE_CLASS_IMPL(FIRST typeclass_info, SECOND typeclass_info, memfn_name) +#define TYPECLASS_BASE_BASE_CLASS_IMPL(typeclass_name, template_args, memfn_name) \ +public \ + virtual TYPECLASS_INTERNAL_REAVER_NAMESPACE::explode, + +#define TYPECLASS_IMPL_BASE_CLASS(x, typeclass_info, memfn_name) TYPECLASS_IMPL_BASE_CLASS_IMPL(FIRST typeclass_info, SECOND typeclass_info, memfn_name) +#define TYPECLASS_IMPL_BASE_CLASS_IMPL(typeclass_name, template_args, memfn_name) \ +public \ + TYPECLASS_INTERNAL_REAVER_NAMESPACE::explode, + +#define TYPECLASS_IMPL_CTOR_INIT(x, typeclass_info, memfn_name) TYPECLASS_IMPL_CTOR_INIT_IMPL(FIRST typeclass_info, SECOND typeclass_info, memfn_name) +#define TYPECLASS_IMPL_CTOR_INIT_IMPL(typeclass_name, template_args, memfn_name) \ + TYPECLASS_INTERNAL_REAVER_NAMESPACE::explode{ t }, + +#define TYPECLASS_ERASED_BASE_CLASS(x, typeclass_info, memfn_name) TYPECLASS_ERASED_BASE_CLASS_IMPL(FIRST typeclass_info, SECOND typeclass_info, memfn_name) +#define TYPECLASS_ERASED_BASE_CLASS_IMPL(typeclass_name, template_args, memfn_name) \ +protected \ + TYPECLASS_INTERNAL_REAVER_NAMESPACE::explode, + +#define TYPECLASS_ERASED_USING(x, typeclass_info, memfn_name) TYPECLASS_ERASED_USING_IMPL(FIRST typeclass_info, SECOND typeclass_info, memfn_name) +#define TYPECLASS_ERASED_USING_IMPL(typeclass_name, template_args, memfn_name) \ + using CONCAT(base_, memfn_name) = reaver::explode; \ + using CONCAT(base_, memfn_name)::memfn_name; + +#define DEFINE_TYPECLASS_SEQ(template_decl, template_args, typeclass_name, member_list) \ + BOOST_PP_SEQ_FOR_EACH(TYPECLASS_PREPARE_MIXINS, typeclass_name, member_list) \ + \ + template_decl struct typeclass_name : BOOST_PP_SEQ_FOR_EACH(TYPECLASS_TYPE_BASE_CLASS, (typeclass_name)(template_args), member_list) \ + TYPECLASS_INTERNAL_REAVER_NAMESPACE::virtual_dtor \ + { \ + TYPECLASS_INSTANCE(typename TYPECLASS_INSTANCE_ARGUMENT); \ + class erased_ref; \ + class erased; \ + \ + BOOST_PP_SEQ_FOR_EACH(TYPECLASS_FRIEND_MIXIN, typeclass_name, member_list) \ + BOOST_PP_SEQ_FOR_EACH(TYPECLASS_TYPE_BASE_USING, (typeclass_name)(template_args), member_list) \ + \ + struct _base : BOOST_PP_SEQ_FOR_EACH(TYPECLASS_BASE_BASE_CLASS, \ + (typeclass_name)(template_args), \ + member_list) TYPECLASS_INTERNAL_REAVER_NAMESPACE::virtual_dtor<_base> \ + { \ + virtual ~_base() = default; \ + virtual std::unique_ptr<_base> clone() const = 0; \ + }; \ + \ + private: \ + template \ + struct _impl : _base, \ + BOOST_PP_SEQ_FOR_EACH(TYPECLASS_IMPL_BASE_CLASS, \ + (typeclass_name)(template_args), \ + member_list) TYPECLASS_INTERNAL_REAVER_NAMESPACE::virtual_dtor<_impl> \ + { \ + _impl(TYPECLASS_T t) \ + : TYPECLASS_INTERNAL_REAVER_NAMESPACE::typeclass_provide_data_member{ std::forward(t) }, \ + BOOST_PP_SEQ_FOR_EACH(TYPECLASS_IMPL_CTOR_INIT, \ + (typeclass_name)(template_args), \ + member_list) TYPECLASS_INTERNAL_REAVER_NAMESPACE::virtual_dtor<_impl>{} \ + { \ + } \ + \ + virtual std::unique_ptr<_base> clone() const override \ + { \ + return std::make_unique<_impl>(TYPECLASS_INTERNAL_REAVER_NAMESPACE::typeclass_provide_data_member::t); \ + } \ + }; \ + }; \ + \ + template_decl class typeclass_name template_args::erased_ref \ + : public typeclass_name, \ + BOOST_PP_SEQ_FOR_EACH(TYPECLASS_ERASED_BASE_CLASS, (typeclass_name)(template_args), member_list) \ + TYPECLASS_INTERNAL_REAVER_NAMESPACE::virtual_dtor \ + { \ + public: \ + template \ + erased_ref(std::reference_wrapper t) \ + : TYPECLASS_INTERNAL_REAVER_NAMESPACE::typeclass_provide_data_member>{ \ + std::make_unique<_impl>(t.get()) \ + } \ + { \ + } \ + \ + template \ + erased_ref(TYPECLASS_T & t) \ + : TYPECLASS_INTERNAL_REAVER_NAMESPACE::typeclass_provide_data_member>{ \ + std::make_unique<_impl>(t) \ + } \ + { \ + } \ + \ + erased_ref(const erased_ref & other) \ + : TYPECLASS_INTERNAL_REAVER_NAMESPACE::typeclass_provide_data_member>{ \ + other.TYPECLASS_INTERNAL_REAVER_NAMESPACE::typeclass_provide_data_member>::t->clone() \ + } \ + { \ + } \ + \ + protected: \ + erased_ref() = default; \ + \ + public: \ + BOOST_PP_SEQ_FOR_EACH(TYPECLASS_ERASED_USING, (typeclass_name)(template_args), member_list) \ + }; \ + \ + template_decl class typeclass_name template_args::erased : public typeclass_name::erased_ref \ + { \ + public: \ + template \ + erased(TYPECLASS_T && t) \ + : TYPECLASS_INTERNAL_REAVER_NAMESPACE::typeclass_provide_data_member>{ \ + std::make_unique<_impl>>(std::forward(t)) \ + } \ + { \ + } \ + \ + template \ + erased(std::reference_wrapper t) \ + : TYPECLASS_INTERNAL_REAVER_NAMESPACE::typeclass_provide_data_member>{ \ + std::make_unique<_impl>(t.get()) \ + } \ + { \ + } \ + }; + +#define TYPECLASS_ERASED_INSTANCE_BASE(x, typeclass_name, memfn_name) \ +public \ + ::reaver:: \ + explode, + +#define TYPECLASS_ERASED_INSTANCE_TEMPLATE_BASE(x, typeclass_info, memfn_name) \ + TYPECLASS_ERASED_INSTANCE_TEMPLATE_BASE_IMPL(FIRST typeclass_info, SECOND typeclass_info, memfn_name) +#define TYPECLASS_ERASED_INSTANCE_TEMPLATE_BASE_IMPL(typeclass_name, template_args, memfn_name) \ +public \ + ::reaver::explode, + +#define DEFINE_TYPECLASS_IMPL(typeclass_name, member_list) \ + DEFINE_TYPECLASS_SEQ(, , typeclass_name, member_list) \ + template<> \ + struct typeclass_name::instance \ + : BOOST_PP_SEQ_FOR_EACH(TYPECLASS_ERASED_INSTANCE_BASE, typeclass_name, member_list)::reaver::unit \ + { \ + }; \ + template<> \ + struct typeclass_name::instance : typeclass_name::instance \ + { \ + } + +#define DEFINE_TYPECLASS(typeclass_name, ...) DEFINE_TYPECLASS_IMPL(typeclass_name, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) + +#define TYPECLASS_TEMPLATE_DECL(X) template +#define TYPECLASS_TEMPLATE_ARGS(X) + +#define DEFINE_TYPECLASS_TEMPLATE_IMPL(template_decl, template_args, typeclass_name, member_list) \ + DEFINE_TYPECLASS_SEQ(TYPECLASS_TEMPLATE_DECL template_decl, TYPECLASS_TEMPLATE_ARGS template_args, typeclass_name, member_list) \ + TYPECLASS_TEMPLATE_DECL template_decl struct typeclass_instance_helper \ + : BOOST_PP_SEQ_FOR_EACH(TYPECLASS_ERASED_INSTANCE_TEMPLATE_BASE, (typeclass_name)(TYPECLASS_TEMPLATE_ARGS template_args), member_list)::reaver::unit \ + { \ + }; \ + TYPECLASS_TEMPLATE_DECL template_decl struct typeclass_instance_helper \ + : typeclass_instance_helper \ + { \ + }; + +#define DEFINE_TYPECLASS_TEMPLATE(template_decl, template_args, typeclass_name, ...) \ + INSTANCE_TEMPLATE_HELPER; \ + DEFINE_TYPECLASS_TEMPLATE_IMPL(template_decl, template_args, typeclass_name, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) +} +} diff --git a/tests/typeclass/typeclass.cpp b/tests/typeclass/typeclass.cpp new file mode 100644 index 0000000..c2790ec --- /dev/null +++ b/tests/typeclass/typeclass.cpp @@ -0,0 +1,189 @@ +/** + * Reaver Library Licence + * + * Copyright © 2017 Michał "Griwes" Dominiak + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation is required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + **/ + +#include "typeclass/typeclass.h" + +#include + +#include + +MAYFLY_BEGIN_SUITE("typeclass"); + +struct counter_definition +{ + using increment = void(); + using get_value = int(); +}; + +DEFINE_TYPECLASS(counter, increment, get_value); + +// clang-format off +DEFAULT_INSTANCE(counter, T) +{ + static void increment(T & t) + { + ++t; + } + + static int get_value(T & t) + { + return t.get_value(); + } +}; +// clang-format on + +class simple_counter : public counter +{ +public: + simple_counter & operator++() + { + ++_value; + return *this; + } + + int get_value() const + { + return _value; + } + +private: + std::uintmax_t _value = 0; +}; + +MAYFLY_ADD_TESTCASE("default implementation", [] { + simple_counter some_counter; + MAYFLY_REQUIRE(counter::get_value(some_counter) == 0); + counter::increment(some_counter); + MAYFLY_REQUIRE(counter::get_value(some_counter) == 1); +}); + +MAYFLY_ADD_TESTCASE("erased", [] { + simple_counter some_counter; + + counter::erased erased = some_counter; + counter::erased_ref erased_ref = some_counter; + + counter::increment(some_counter); + counter::increment(erased); + counter::increment(erased_ref); + + MAYFLY_REQUIRE(counter::get_value(erased) == 1); + MAYFLY_REQUIRE(counter::get_value(erased_ref) == 2); + MAYFLY_REQUIRE(counter::get_value(some_counter) == 2); +}); + +class broken_counter +{ +public: + TYPECLASS_INSTANCE(typename T); + + int get_value() const + { + return 123; + } +}; + +// clang-format off +INSTANCE(counter, broken_counter) +{ + static void increment(broken_counter &) + { + throw 1; + } +}; +// clang-format on + +MAYFLY_ADD_TESTCASE("overriden implementation", [] { + broken_counter some_counter; + MAYFLY_REQUIRE(counter::get_value(some_counter) == 123); + MAYFLY_REQUIRE_THROWS_TYPE(int, counter::increment(some_counter)); + MAYFLY_REQUIRE(counter::get_value(some_counter) == 123); +}); + +template +struct sizeof_counter_definition +{ + using increment = void(); + using get_value = int(); +}; + +DEFINE_TYPECLASS_TEMPLATE((typename T), (T), sizeof_counter, increment, get_value); + +// clang-format off +DEFAULT_INSTANCE_TEMPLATE((typename T), (T), sizeof_counter, U) +{ + static void increment(U & u) + { + ++u; + } + + static int get_value(U & u) + { + return u.get_value(); + } +}; +// clang-format on + +template +struct some_sizeof_counter : sizeof_counter +{ + TYPECLASS_INSTANCE_TEMPLATE((typename U), (U), some_sizeof_counter); + + void increment() + { + _value += sizeof(T); + } + + int get_value() const + { + return _value; + } + +private: + int _value = 0; +}; + +// clang-format off +INSTANCE_TEMPLATE((typename T), (T), sizeof_counter, some_sizeof_counter) +{ + static void increment(some_sizeof_counter & t) + { + t.increment(); + } +}; +// clang-format on + +MAYFLY_ADD_TESTCASE("typeclass template", [] { + some_sizeof_counter some_counter; + MAYFLY_REQUIRE(sizeof_counter::get_value(some_counter) == 0); + sizeof_counter::increment(some_counter); + MAYFLY_REQUIRE(sizeof_counter::get_value(some_counter) == sizeof(int)); + + sizeof_counter::erased erased = some_counter; + sizeof_counter::erased_ref erased_ref = some_counter; + + sizeof_counter::increment(some_counter); + MAYFLY_REQUIRE(sizeof_counter::get_value(erased) == sizeof(int)); + MAYFLY_REQUIRE(sizeof_counter::get_value(erased_ref) == sizeof(int) * 2); +}); + +MAYFLY_END_SUITE; From 8f8596670ed56a65ff11c50885e8df98a17e8903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=27Griwes=27=20Dominiak?= Date: Mon, 13 Mar 2017 21:03:49 +0100 Subject: [PATCH 2/3] typeclass: workaround for Clang 3.9. --- include/reaver/typeclass/typeclass.h | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/include/reaver/typeclass/typeclass.h b/include/reaver/typeclass/typeclass.h index 1ab9b04..277b6f3 100644 --- a/include/reaver/typeclass/typeclass.h +++ b/include/reaver/typeclass/typeclass.h @@ -144,6 +144,10 @@ inline namespace _v1 } \ }; \ \ + /* workaround for Clang <4.0 */ \ + template \ + using CONCAT3(typeclass_name, _typeclass_provide_alias_, memfn_name) = CONCAT3(typeclass_name, _typeclass_provide_, memfn_name); \ + \ template \ struct CONCAT3(typeclass_name, _typeclass_erased_provide_, memfn_name) \ : protected TYPECLASS_INTERNAL_REAVER_NAMESPACE::virtual_dtor, \ @@ -156,6 +160,10 @@ inline namespace _v1 } \ }; \ \ + /* also a workaround for Clang <4.0 */ \ + template \ + using CONCAT3(typeclass_name, _typeclass_erased_provide_alias_, memfn_name) = CONCAT3(typeclass_name, _typeclass_erased_provide_, memfn_name); \ + \ template \ struct CONCAT3(typeclass_name, _typeclass_erased_instance_provide_, memfn_name) \ { \ @@ -193,10 +201,8 @@ private #define TYPECLASS_TYPE_BASE_USING(x, typeclass_info, memfn_name) TYPECLASS_TYPE_BASE_USING_IMPL(FIRST typeclass_info, SECOND typeclass_info, memfn_name) #define TYPECLASS_TYPE_BASE_USING_IMPL(typeclass_name, template_args, memfn_name) \ - using TYPECLASS_INTERNAL_REAVER_NAMESPACE::explode< \ - typename CONCAT(typeclass_name, _definition) \ - template_args::memfn_name, /*reaver::strip_arguments::template type,*/ \ - CONCAT3(typeclass_name, _typeclass_provide_, memfn_name), \ + using TYPECLASS_INTERNAL_REAVER_NAMESPACE::explode::memfn_name; #define TYPECLASS_BASE_BASE_CLASS(x, typeclass_info, memfn_name) TYPECLASS_BASE_BASE_CLASS_IMPL(FIRST typeclass_info, SECOND typeclass_info, memfn_name) @@ -229,10 +235,9 @@ protected #define TYPECLASS_ERASED_USING(x, typeclass_info, memfn_name) TYPECLASS_ERASED_USING_IMPL(FIRST typeclass_info, SECOND typeclass_info, memfn_name) #define TYPECLASS_ERASED_USING_IMPL(typeclass_name, template_args, memfn_name) \ - using CONCAT(base_, memfn_name) = reaver::explode; \ - using CONCAT(base_, memfn_name)::memfn_name; + using TYPECLASS_INTERNAL_REAVER_NAMESPACE::explode::memfn_name; #define DEFINE_TYPECLASS_SEQ(template_decl, template_args, typeclass_name, member_list) \ BOOST_PP_SEQ_FOR_EACH(TYPECLASS_PREPARE_MIXINS, typeclass_name, member_list) \ From f2218298b6b89e9af3d81d306fbe9c27979d688b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=27Griwes=27=20Dominiak?= Date: Thu, 4 May 2017 15:11:15 +0200 Subject: [PATCH 3/3] typeclass: some more typeclasses. --- doc/licence-cpp-formatted | 2 +- include/reaver/sfinae_function.h | 29 ++++++++++ include/reaver/typeclass/functor.h | 58 +++++++++++++++++++ include/reaver/typeclass/hashable.h | 60 ++++++++++++++++++++ include/reaver/typeclass/swappable.h | 68 ++++++++++++++++++++++ include/reaver/typeclass/typeclass.h | 11 ++-- tests/typeclass/functor.cpp | 70 +++++++++++++++++++++++ tests/typeclass/hashable.cpp | 84 ++++++++++++++++++++++++++++ tests/typeclass/swappable.cpp | 79 ++++++++++++++++++++++++++ 9 files changed, 453 insertions(+), 8 deletions(-) create mode 100644 include/reaver/sfinae_function.h create mode 100644 include/reaver/typeclass/functor.h create mode 100644 include/reaver/typeclass/hashable.h create mode 100644 include/reaver/typeclass/swappable.h create mode 100644 tests/typeclass/functor.cpp create mode 100644 tests/typeclass/hashable.cpp create mode 100644 tests/typeclass/swappable.cpp diff --git a/doc/licence-cpp-formatted b/doc/licence-cpp-formatted index 2acc7a0..af9b8f0 100644 --- a/doc/licence-cpp-formatted +++ b/doc/licence-cpp-formatted @@ -1,7 +1,7 @@ /** * Reaver Library Licence * - * Copyright © 2016 Michał "Griwes" Dominiak + * Copyright © 2017 Michał "Griwes" Dominiak * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages diff --git a/include/reaver/sfinae_function.h b/include/reaver/sfinae_function.h new file mode 100644 index 0000000..bc3f09b --- /dev/null +++ b/include/reaver/sfinae_function.h @@ -0,0 +1,29 @@ +/** + * Reaver Library Licence + * + * Copyright © 2017 Michał "Griwes" Dominiak + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation is required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + **/ + +#pragma once + +#define SFINAE_FUNCTION(...) \ + noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) \ + { \ + return __VA_ARGS__; \ + } diff --git a/include/reaver/typeclass/functor.h b/include/reaver/typeclass/functor.h new file mode 100644 index 0000000..6f033d4 --- /dev/null +++ b/include/reaver/typeclass/functor.h @@ -0,0 +1,58 @@ +/** + * Reaver Library Licence + * + * Copyright © 2017 Michał "Griwes" Dominiak + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation is required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + **/ + +#pragma once + +#include "typeclass.h" + +namespace reaver +{ +inline namespace prelude +{ + inline namespace _v1 + { + template typename F> + struct functor + { + TYPECLASS_INSTANCE(typename U); + }; + + template typename F, + typename A, + typename... Args, + typename Fun, + decltype(tc_instance, F>::fmap(std::declval>(), std::declval()), int())...> + auto fmap(F f, Fun && fn) + { + return tc_instance, F>::fmap(std::move(f), std::forward(fn)); + } + + // clang-format off + DEFAULT_INSTANCE_TEMPLATE((template typename F), (F), functor, U) + { + template + static auto fmap(F, Fun && fn) = delete; + }; + // clang-format on + } +} +} diff --git a/include/reaver/typeclass/hashable.h b/include/reaver/typeclass/hashable.h new file mode 100644 index 0000000..bb5fee3 --- /dev/null +++ b/include/reaver/typeclass/hashable.h @@ -0,0 +1,60 @@ +/** + * Reaver Library Licence + * + * Copyright © 2017 Michał "Griwes" Dominiak + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation is required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + **/ + +#pragma once + +#include "sfinae_function.h" +#include "typeclass.h" + +namespace reaver +{ +inline namespace prelude +{ + inline namespace _v1 + { + // TODO: use this + struct hashable_definition + { + using hash = std::size_t() const; + }; + + struct hashable + { + TYPECLASS_INSTANCE(typename T); + }; + + template + auto hash(const T & t) SFINAE_FUNCTION(tc_instance::hash(t)); + + // clang-format off + DEFAULT_INSTANCE(hashable, T) + { + // prefer std::hash specialization over T::hash_value + template + static auto hash(const U & t, Ts...) SFINAE_FUNCTION(t.hash_value()); + + template + static auto hash(const U & t) SFINAE_FUNCTION(std::hash()(t)); + }; + } +} +} diff --git a/include/reaver/typeclass/swappable.h b/include/reaver/typeclass/swappable.h new file mode 100644 index 0000000..7da7474 --- /dev/null +++ b/include/reaver/typeclass/swappable.h @@ -0,0 +1,68 @@ +/** + * Reaver Library Licence + * + * Copyright © 2017 Michał "Griwes" Dominiak + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation is required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + **/ + +#pragma once + +#include "sfinae_function.h" +#include "typeclass.h" + +namespace reaver +{ +inline namespace prelude +{ + inline namespace _v1 + { + struct swappable + { + TYPECLASS_INSTANCE(typename T); + }; + + template + auto swap(T & a, T & b) SFINAE_FUNCTION(tc_instance::swap(a, b)); + + // clang-format off + DEFAULT_INSTANCE(swappable, T) + { + template + struct has_member_swap : std::false_type {}; + + template + struct has_member_swap().swap(std::declval()))>> : std::true_type {}; + + template::value, int>::type...> + static void swap(T & a, T & b) + noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v) + { + auto tmp = std::move(a); + a = std::move(b); + b = std::move(tmp); + } + + template::value, int>::type...> + static void swap(T & a, T & b) noexcept(noexcept(a.swap(b))) + { + a.swap(b); + } + }; + } +} +} diff --git a/include/reaver/typeclass/typeclass.h b/include/reaver/typeclass/typeclass.h index 277b6f3..fe6de0a 100644 --- a/include/reaver/typeclass/typeclass.h +++ b/include/reaver/typeclass/typeclass.h @@ -38,18 +38,15 @@ namespace reaver inline namespace _v1 { template - struct typeclass_trait; - - template - struct typeclass_trait::value>::type> + struct typeclass_trait { - using type = typename T::template instance; + using type = typename Typeclass::template instance; }; template - struct typeclass_trait::value>::type> + struct typeclass_trait>> { - using type = typename Typeclass::template instance; + using type = typename T::template instance; }; template diff --git a/tests/typeclass/functor.cpp b/tests/typeclass/functor.cpp new file mode 100644 index 0000000..03b8992 --- /dev/null +++ b/tests/typeclass/functor.cpp @@ -0,0 +1,70 @@ +/** + * Reaver Library Licence + * + * Copyright © 2017 Michał "Griwes" Dominiak + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation is required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + **/ + +#include + +#include "typeclass/functor.h" + +MAYFLY_BEGIN_SUITE("typeclass"); +MAYFLY_BEGIN_SUITE("functor"); + +INSTANCE_TEMPLATE_HELPER; + +template +struct silly_wrapper +{ + TYPECLASS_INSTANCE_TEMPLATE((typename U), (silly_wrapper), reaver::functor); + + silly_wrapper(T t) : t{ std::move(t) } + { + } + + T t; +}; + +// clang-format off +INSTANCE_TEMPLATE((typename T), (silly_wrapper), reaver::functor, silly_wrapper) +{ + template + static auto fmap(silly_wrapper a, Fun && fn) + { + using wrapped = decltype(std::forward(fn)(a.t)); + return silly_wrapper{ std::forward(fn)(a.t) }; + } +}; +// clang-format on + +MAYFLY_ADD_TESTCASE("silly wrapper", [] { + silly_wrapper sw1{ 1 }; + auto sw2 = reaver::fmap(sw1, [](auto && i) { return i * 2 + 1; }); + + MAYFLY_REQUIRE(std::is_same>::value); + MAYFLY_REQUIRE(sw2.t == 3); + + auto sw3 = reaver::fmap(sw2, [](auto && i) { return std::to_string(i); }); + + MAYFLY_REQUIRE(std::is_same>::value); + MAYFLY_REQUIRE(sw3.t == "3"); +}); + +MAYFLY_END_SUITE; +MAYFLY_END_SUITE; diff --git a/tests/typeclass/hashable.cpp b/tests/typeclass/hashable.cpp new file mode 100644 index 0000000..13d649f --- /dev/null +++ b/tests/typeclass/hashable.cpp @@ -0,0 +1,84 @@ +/** + * Reaver Library Licence + * + * Copyright © 2017 Michał "Griwes" Dominiak + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation is required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + **/ + +#include + +#include "typeclass/hashable.h" + +namespace +{ +struct test_with_specialization +{ + std::size_t hash_value() const + { + MAYFLY_REQUIRE(!"should've preferred the std::hash specialization!"); + __builtin_unreachable(); + } +}; + +struct test_without_specialization +{ + std::size_t value; + + std::size_t hash_value() const + { + return value; + } +}; +} + +namespace std +{ +template<> +struct hash +{ + std::size_t operator()(const test_with_specialization &) const + { + return 0; + } +}; +} + +MAYFLY_BEGIN_SUITE("typeclass"); +MAYFLY_BEGIN_SUITE("hashable"); + +MAYFLY_ADD_TESTCASE("string", [] { + std::string s1 = "foo"; + std::string s2 = "foo"; + std::string s3 = "bar"; + + MAYFLY_REQUIRE(reaver::hash(s1) == reaver::hash(s2)); + MAYFLY_REQUIRE(reaver::hash(s1) != reaver::hash(s3)); +}); + +MAYFLY_ADD_TESTCASE("std::hash should win", [] { + test_with_specialization t; + MAYFLY_REQUIRE(reaver::hash(t) == 0); +}); + +MAYFLY_ADD_TESTCASE("hash_value", [] { + test_without_specialization t{ 123 }; + MAYFLY_REQUIRE(reaver::hash(t) == 123); +}); + +MAYFLY_END_SUITE; +MAYFLY_END_SUITE; diff --git a/tests/typeclass/swappable.cpp b/tests/typeclass/swappable.cpp new file mode 100644 index 0000000..1798e7b --- /dev/null +++ b/tests/typeclass/swappable.cpp @@ -0,0 +1,79 @@ +/** + * Reaver Library Licence + * + * Copyright © 2017 Michał "Griwes" Dominiak + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation is required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + **/ + +#include + +#include "typeclass/swappable.h" + +MAYFLY_BEGIN_SUITE("typeclass"); +MAYFLY_BEGIN_SUITE("swappable"); + +MAYFLY_ADD_TESTCASE("default swap", [] { + int a = 1; + int b = 2; + + reaver::swap(a, b); + + MAYFLY_REQUIRE(a == 2); + MAYFLY_REQUIRE(b == 1); +}); + +MAYFLY_ADD_TESTCASE("default member swap", [] { + std::vector v1{ 1, 2, 3 }; + std::vector v2{ 4, 5, 6 }; + + reaver::swap(v1, v2); + + MAYFLY_REQUIRE(v1 == std::vector{ 4, 5, 6 }); + MAYFLY_REQUIRE(v2 == std::vector{ 1, 2, 3 }); +}); + +struct swap_test +{ + TYPECLASS_INSTANCE(typename T); + + swap_test * swapped_with = nullptr; +}; + +// clang-format off +INSTANCE(reaver::swappable, swap_test) +{ + static void swap(swap_test & lhs, swap_test & rhs) + { + lhs.swapped_with = &rhs; + rhs.swapped_with = &lhs; + } +}; +// clang-format on + +MAYFLY_ADD_TESTCASE("custom swap", [] { + swap_test a; + swap_test b; + + reaver::swap(a, b); + + MAYFLY_REQUIRE(a.swapped_with == &b); + MAYFLY_REQUIRE(b.swapped_with == &a); +}); + +MAYFLY_END_SUITE; +MAYFLY_END_SUITE;