Initial commit

This commit is contained in:
Bassem Girgis
2018-12-20 17:34:07 -06:00
parent 7a2d899662
commit 81b4b9e273
34743 changed files with 5940233 additions and 0 deletions

View File

@@ -0,0 +1,560 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_CONTAINER_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_CONTAINER_HPP
#include "detail/traits.hpp"
#include "detail/service_traits.hpp"
#include "detail/utils.hpp"
#include "detail/container_service.hpp"
#include "detail/invoke.hpp"
#include "detail/single.hpp"
#include "detail/injected.hpp"
#include "detail/error.hpp"
#include "predicate.hpp"
#include <unordered_map>
#include <memory>
#include <type_traits>
#include <tuple>
#include <algorithm>
#include <vector>
namespace kgr {
struct Container final {
private:
template<typename Condition, typename T = int> using enable_if = detail::enable_if_t<Condition::value, T>;
template<typename Condition, typename T = int> using disable_if = detail::enable_if_t<!Condition::value, T>;
template<typename T> using instance_ptr = std::unique_ptr<T, void(*)(void*)>;
using instance_cont = std::vector<instance_ptr<void>>;
using service_cont = std::unordered_map<type_id_t, void*>;
template<typename T> using contained_service_t = typename std::conditional<
detail::is_single<T>::value,
instance_ptr<detail::SingleInjected<T>>,
detail::Injected<T>>::type;
template<typename T>
static void deleter(void* i) {
delete static_cast<T*>(i);
}
template<typename T, typename... Args>
static instance_ptr<detail::SingleInjected<T>> make_instance_ptr(Args&&... args) {
return instance_ptr<detail::SingleInjected<T>>{
new detail::SingleInjected<T>{std::forward<Args>(args)...},
&Container::deleter<detail::BaseInjected<T>>
};
}
template<typename T, typename C, typename... Args>
static instance_ptr<detail::BaseInjected<C>> make_override_ptr(Args&&... args) {
return instance_ptr<detail::ServiceOverride<T, C>>{
new detail::ServiceOverride<T, C>{std::forward<Args>(args)...},
&Container::deleter<detail::BaseInjected<C>>
};
}
public:
explicit Container() = default;
Container(const Container &) = delete;
Container& operator=(const Container &) = delete;
Container(Container&&) = default;
Container& operator=(Container&&) = default;
~Container() = default;
/*
* This function construct a service definition with the provided arguments.
* It also saves it in the container.
* It returns void.
* This function only works with single services.
* This version of the function will call functions in autocall.
*/
template<typename T, typename... Args,
enable_if<detail::is_single<T>> = 0,
enable_if<detail::is_service<T>> = 0,
enable_if<detail::is_service_instantiable<T, Args...>> = 0>
void instance(Args&&... args) {
autocall(save_instance<T>(make_contained_service<T>(std::forward<Args>(args)...)).get());
}
/*
* This function construct a service definition with the provided arguments.
* It also saves it in the container.
* It returns void.
* This function only works with single services.
* This version of the function will not call functions in autocall.
*/
template<typename T, typename... Args,
enable_if<detail::is_single<T>> = 0,
enable_if<detail::is_service<T>> = 0,
enable_if<detail::is_service_instantiable<T, Args...>> = 0>
void instance(detail::no_autocall_t, Args&&... args) {
save_instance<T>(make_contained_service<T>(std::forward<Args>(args)...));
}
/*
* This function returns the service given by serivce definition T.
* T must be a valid service and must be constructible with arguments passed as parameters
* In case of a non-single service, it takes additional arguments to be sent to the T::construct function.
* T must not be a polymorphic type.
*/
template<typename T, typename... Args, enable_if<detail::is_service_valid<T, Args...>> = 0>
ServiceType<T> service(Args&&... args) {
return definition<T>(std::forward<Args>(args)...).forward();
}
/*
* The following two overloads are called in a case where the service is invalid,
* or is called when provided arguments don't match the constructor.
* In GCC, a diagnostic is provided.
*/
template<typename T, enable_if<std::is_default_constructible<detail::ServiceError<T>>> = 0>
detail::Sink service(detail::ServiceError<T> = {}) = delete;
template<typename T, typename... Args>
detail::Sink service(detail::ServiceError<T, detail::identity_t<Args>...>, Args&&...) = delete;
/*
* This function returns the result of the callable object of type U.
* Args are additional arguments to be sent to the function after services arguments.
* This function will deduce arguments from the function signature.
*/
template<template<typename> class Map = AdlMap, typename U, typename... Args, enable_if<detail::is_invoke_valid<Map, detail::decay_t<U>, Args...>> = 0>
detail::invoke_function_result_t<Map, detail::decay_t<U>, Args...> invoke(U&& function, Args&&... args) {
return invoke_helper<Map>(
detail::tuple_seq_minus<detail::invoke_function_arguments_t<Map, detail::decay_t<U>, Args...>, sizeof...(Args)>{},
std::forward<U>(function),
std::forward<Args>(args)...
);
}
/*
* This function returns the result of the callable object of type U.
* This version of the function is called when `function` is not invokable to provide diagnostic.
*/
template<template<typename> class Map>
detail::Sink invoke(detail::NotInvokableError = {}, ...) = delete;
/*
* This function returns the result of the callable object of type U.
* It will call the function with the sevices listed in the `Services` parameter pack.
* It will call it in a equivalent expression of `std::declval<U>()(std::declval<ServiceType<Services>>()..., std::declval<Args>()...)`
*/
template<typename... Services, typename U, typename... Args, detail::int_t<
enable_if<detail::is_service_valid<Services>>...,
detail::enable_if_t<(sizeof...(Services) > 0)>> = 0>
auto invoke(U&& function, Args&&... args) -> decltype(std::declval<U>()(std::declval<ServiceType<Services>>()..., std::declval<Args>()...)) {
return std::forward<U>(function)(service<Services>()..., std::forward<Args>(args)...);
}
/*
* This oveload is called when one of the services to be injected is invalid.
* It will provide diagnostic on GCC.
*/
template<typename... Services>
detail::Sink invoke(detail::NotInvokableError = {}, ...) = delete;
/*
* This function clears this container.
* Every single services are invalidated after calling this function.
*/
inline void clear() {
_instances.clear();
_services.clear();
}
/*
* This function fork the container into a new container.
* The new container will have the copied state of the first container.
* Construction of new services within the new container will not affect the original one.
* The new container must exist within the lifetime of the original container.
*
* It takes a predicate type as template argument.
* The default predicate is kgr::All.
*
* This version of the function takes a predicate that is default constructible.
* It will call fork() with a predicate as parameter.
*/
template<typename Predicate = All, enable_if<std::is_default_constructible<Predicate>> = 0>
Container fork() const {
return fork(Predicate{});
}
/*
* This function fork the container into a new container.
* The new container will have the copied state of the first container.
* Construction of new services within the new container will not affect the original one.
* The new container must exist within the lifetime of the original container.
*
* It takes a predicate as argument.
*/
template<typename Predicate>
Container fork(Predicate&& predicate) const {
Container c;
c._services.reserve(_services.size());
std::copy_if(
_services.begin(), _services.end(),
std::inserter(c._services, c._services.begin()),
[&predicate](service_cont::const_reference i){
// We don't forward the predicate here, we use it many times.
return predicate(i.first);
}
);
return c;
}
/*
* This function merges a container with another.
* The receiving container will prefer it's own instances in a case of conflicts.
*/
inline void merge(Container&& other) {
_instances.insert(_instances.end(), std::make_move_iterator(other._instances.begin()), std::make_move_iterator(other._instances.end()));
_services.insert(other._services.begin(), other._services.end());
}
/*
* This function return true if the container contains the service T.
* T nust be a single service.
*/
template<typename T, enable_if<detail::is_service<T>> = 0, enable_if<detail::is_single<T>> = 0>
bool contains() const {
return _services.find(type_id<T>) != _services.end();
}
private:
///////////////////////
// save instance //
///////////////////////
/*
* This function will create a new instance and save it.
* It also returns a reference to the constructed service.
*/
template<typename T, typename... Args, enable_if<detail::is_single<T>> = 0, disable_if<detail::is_abstract_service<T>> = 0>
detail::BaseInjected<T>& save_new_instance(Args&&... args) {
auto& service = save_instance<T>(make_service_instance<T>(std::forward<Args>(args)...));
autocall(service.get());
return service;
}
/*
* This function is a specialization of save_new_instance for abstract classes.
* Since you cannot construct an abstract class, this function always throw.
*/
template<typename T, typename... Args,
enable_if<detail::is_single<T>> = 0,
enable_if<detail::is_abstract_service<T>> = 0,
disable_if<detail::has_default<T>> = 0>
detail::BaseInjected<T>& save_new_instance(Args&&...) {
throw std::out_of_range{"No instance found for the requested abstract service"};
}
/*
* This function is a specialization of save_new_instance for abstract classes.
* Since that abstract service has a default service specified, we can contruct that one.
*/
template<typename T, typename... Args,
enable_if<detail::is_single<T>> = 0,
enable_if<detail::is_abstract_service<T>> = 0,
enable_if<detail::has_default<T>> = 0>
detail::BaseInjected<T>& save_new_instance(Args&&...) {
save_new_instance<detail::default_type<T>>();
// The static assert is still required here, if other checks fails and allow
// a call to this function where the default service don't overrides T, it would be UB.
static_assert(
detail::is_overriden_by<T, detail::default_type<T>>::value,
"The default service type of an abstract service must override that abstract serivce."
);
// This could be faster if we had access to instance of override services.
return *static_cast<detail::BaseInjected<T>*>(_services[type_id<T>]);
}
/*
* This function will save the instance sent as arguments.
* It receive a service that overrides.
* It will save the instance and will save overrides if any.
*/
template<typename T>
detail::SingleInjected<T>& save_instance(contained_service_t<T> service) {
return save_instance<T>(detail::tuple_seq<detail::parent_types<T>>{}, std::move(service));
}
/*
* This function saves the instance and it's overrides if any.
* It starts the iteration that save each overrides and the service itself.
*/
template<typename T, std::size_t... S>
detail::SingleInjected<T>& save_instance(detail::seq<S...>, contained_service_t<T> service) {
return save_instance_helper<T, detail::meta_list_element_t<S, detail::parent_types<T>>...>(std::move(service));
}
/*
* This function save the instance into the container.
* It also returns a reference to the instance inside the container.
*/
template<typename T>
detail::SingleInjected<T>& save_instance_helper(contained_service_t<T> service) {
auto& serviceRef = *service;
instance_ptr<detail::BaseInjected<T>> injectedTypeService = std::move(service);
_services[type_id<T>] = injectedTypeService.get();
_instances.emplace_back(std::move(injectedTypeService));
return serviceRef;
}
/*
* This function is the iteration that saves overrides to the container.
* It will call itself until there is no override left.
* It will class save_instance_helper with one template argument as it's last iteration.
*/
template<typename T, typename Override, typename... Others>
detail::SingleInjected<T>& save_instance_helper(contained_service_t<T> service) {
auto overrideService = make_override_ptr<T, Override>(service->get());
static_assert(
std::is_same<instance_ptr<detail::BaseInjected<Override>>, decltype(overrideService)>::value,
"the override service must be the type instance_ptr<detail::BaseInjected<Override>>"
);
_services[type_id<Override>] = overrideService.get();
_instances.emplace_back(std::move(overrideService));
return save_instance_helper<T, Others...>(std::move(service));
}
///////////////////////
// service //
///////////////////////
/*
* This function call service using the service map.
* This function is called when the service map `Map` is valid for a given `T`
*/
template<template<typename> class Map, typename T, enable_if<detail::is_complete_map<Map, T>> = 0>
auto mapped_service() -> decltype(service<detail::service_map_t<Map, T>>()) {
return service<detail::service_map_t<Map, T>>();
}
///////////////////////
// definition //
///////////////////////
/*
* This function returns a service definition.
* This version of this function create the service each time it is called.
*/
template<typename T, typename... Args, disable_if<detail::is_single<T>> = 0, disable_if<detail::is_container_service<T>> = 0>
detail::Injected<T> definition(Args&&... args) {
auto service = make_service_instance<T>(std::forward<Args>(args)...);
autocall(service.get());
return service;
}
/*
* This function returns a service definition.
* This version of this function is specific to a container service.
*/
template<typename T, enable_if<detail::is_container_service<T>> = 0>
detail::Injected<T> definition() {
return detail::Injected<T>{T{*this}};
}
/*
* This function returns a service definition.
* This version of this function create the service if it was not created before.
*/
template<typename T, enable_if<detail::is_single<T>> = 0, disable_if<detail::is_container_service<T>> = 0>
detail::BaseInjected<T>& definition() {
if (auto service = _services[type_id<T>]) {
return *static_cast<detail::BaseInjected<T>*>(service);
} else {
return save_new_instance<T>();
}
}
///////////////////////
// make instance //
///////////////////////
/*
* This function creates an instance of a service.
* It forward the work to make_service_instance_helper with an integer sequence.
*/
template<typename T, typename... Args>
contained_service_t<T> make_service_instance(Args&&... args) {
return make_service_instance_helper<T>(detail::construct_result_seq<T, Args...>{}, std::forward<Args>(args)...);
}
/*
* This function is the helper for make_service_instance.
* It construct the service using the values returned by construct.
* It forward it's work to make_contained_service.
*/
template<typename T, typename... Args, std::size_t... S>
contained_service_t<T> make_service_instance_helper(detail::seq<S...>, Args&&... args) {
auto constructArgs = invoke_raw(detail::construct_function<T, Args...>::value, std::forward<Args>(args)...);
// This line is used to shut unused-variable warning, since S can be empty.
static_cast<void>(constructArgs);
return make_contained_service<T>(std::forward<detail::tuple_element_t<S, decltype(constructArgs)>>(std::get<S>(constructArgs))...);
}
/*
* This function create a service with the received arguments.
* It creating it in the right type for the container to contain it in it's container.
*/
template<typename T, typename... Args,
enable_if<detail::is_single<T>> = 0,
enable_if<detail::is_someway_constructible<T, in_place_t, Args...>> = 0>
contained_service_t<T> make_contained_service(Args&&... args) {
return make_instance_ptr<T>(in_place, std::forward<Args>(args)...);
}
/*
* This function create a service with the received arguments.
* It creating it in the right type for the container return it and inject it without overhead.
*/
template<typename T, typename... Args,
disable_if<detail::is_single<T>> = 0,
enable_if<detail::is_someway_constructible<T, in_place_t, Args...>> = 0>
contained_service_t<T> make_contained_service(Args&&... args) {
return detail::Injected<T>{in_place, std::forward<Args>(args)...};
}
/*
* This function create a service with the received arguments.
* It creating it in the right type for the container to contain it in it's container.
* This version of the function is called when the service definition has no valid constructor.
* It will try to call an emplace function that construct the service in a lazy way.
*/
template<typename T, typename... Args,
enable_if<detail::is_single<T>> = 0,
disable_if<detail::is_someway_constructible<T, in_place_t, Args...>> = 0,
enable_if<detail::is_emplaceable<T, Args...>> = 0>
contained_service_t<T> make_contained_service(Args&&... args) {
auto service = make_instance_ptr<T>();
service->get().emplace(std::forward<Args>(args)...);
return service;
}
/*
* This function create a service with the received arguments.
* It creating it in the right type for the container return it and inject it without overhead.
* This version of the function is called when the service definition has no valid constructor.
* It will try to call an emplace function that construct the service in a lazy way.
*/
template<typename T, typename... Args,
disable_if<detail::is_single<T>> = 0,
disable_if<detail::is_someway_constructible<T, in_place_t, Args...>> = 0,
enable_if<detail::is_emplaceable<T, Args...>> = 0>
contained_service_t<T> make_contained_service(Args&&... args) {
detail::Injected<T> service;
service.get().emplace(std::forward<Args>(args)...);
return service;
}
///////////////////////
// invoke //
///////////////////////
/*
* This function is an helper for the public invoke function.
* It unpacks arguments of the function with an integer sequence.
*/
template<template<typename> class Map, typename U, typename... Args, std::size_t... S>
detail::invoke_function_result_t<Map, detail::decay_t<U>, Args...> invoke_helper(detail::seq<S...>, U&& function, Args&&... args) {
return std::forward<U>(function)(
mapped_service<Map, detail::invoke_function_argument_t<S, Map, detail::decay_t<U>, Args...>>()...,
std::forward<Args>(args)...
);
}
/*
* This function is the same as invoke but it sends service definitions instead of the service itself.
* It is called with some autocall function and the make_service_instance function.
*/
template<typename U, typename... Args>
detail::function_result_t<detail::decay_t<U>> invoke_raw(U&& function, Args&&... args) {
return invoke_raw_helper(
detail::tuple_seq_minus<detail::function_arguments_t<detail::decay_t<U>>, sizeof...(Args)>{},
std::forward<U>(function),
std::forward<Args>(args)...
);
}
/*
* This function is an helper of the invoke_raw function.
* It unpacks arguments of the function U with an integer sequence.
*/
template<typename U, typename... Args, std::size_t... S>
detail::function_result_t<detail::decay_t<U>> invoke_raw_helper(detail::seq<S...>, U&& function, Args&&... args) {
return std::forward<U>(function)(definition<detail::injected_argument_t<S, detail::decay_t<U>>>()..., std::forward<Args>(args)...);
}
///////////////////////
// autocall //
///////////////////////
/*
* This function starts the iteration (autocall_helper).
*/
template<typename T, enable_if<detail::has_autocall<detail::decay_t<T>>> = 0>
void autocall(T&& service) {
autocall(detail::tuple_seq<typename detail::decay_t<T>::Autocall>{}, std::forward<T>(service));
}
/*
* This function is the iteration for autocall.
*/
template<typename T, std::size_t... S, enable_if<detail::has_autocall<detail::decay_t<T>>> = 0>
void autocall(detail::seq<S...>, T&& service) {
using U = detail::decay_t<T>;
using unpack = int[];
(void)unpack{(invoke_autocall(
detail::function_seq<detail::autocall_nth_function_t<U, S>>{}, std::forward<T>(service), detail::autocall_nth_function<U, S>::value
), 0)..., 0};
}
/*
* This function is the invoke_autocall that take the method to invoke as parameter.
* It invokes the function sent as parameter.
*/
template<typename T, typename F, std::size_t... S>
void invoke_autocall(detail::seq<S...>, T&& service, F&& function) {
invoke_raw([&service, &function](detail::function_argument_t<S, detail::decay_t<F>>... args) {
(std::forward<T>(service).*std::forward<F>(function))(std::forward<decltype(args)>(args)...);
});
}
/*
* This function is called when there is no autocall to do.
*/
template<typename T, disable_if<detail::has_autocall<detail::decay_t<T>>> = 0>
void autocall(T&&) {}
///////////////////////
// instances //
///////////////////////
instance_cont _instances;
service_cont _services;
};
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_CONTAINER_HPP

View File

@@ -0,0 +1,32 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_DEBUG_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_DEBUG_HPP
#include "detail/error.hpp"
namespace kgr {
namespace debug {
template<typename T, typename U, typename... Args>
auto service(U&& u, Args&&...) -> detail::enable_if_t<std::is_constructible<detail::ServiceError<T, Args...>, U>::value> {
detail::ServiceError<T, Args...> error{std::forward<U>(u)};
static_cast<void>(error);
}
template<typename T, typename U, typename... Args>
auto service(U&&, Args&&...) -> detail::enable_if_t<!std::is_constructible<detail::ServiceError<T, Args...>, U>::value> = delete;
template<typename T>
auto service() -> detail::enable_if_t<std::is_default_constructible<detail::ServiceError<T>>::value> {
detail::ServiceError<T> error{};
static_cast<void>(error);
}
template<typename T>
auto service() -> detail::enable_if_t<!std::is_default_constructible<detail::ServiceError<T>>::value> = delete;
}
}
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_DEBUG_HPP

View File

@@ -0,0 +1,32 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_CONTAINER_SERVICE_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_CONTAINER_SERVICE_HPP
#include "utils.hpp"
#include "injected.hpp"
namespace kgr {
struct Container;
namespace detail {
struct ContainerServiceTag {};
} // namespace detail
struct ContainerService : detail::ContainerServiceTag {
explicit ContainerService(Container& instance) : _instance{instance} {}
inline Container& forward() {
return _instance;
}
private:
Container& _instance;
};
auto service_map(const Container&) -> ContainerService;
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_CONTAINER_SERVICE_HPP

View File

@@ -0,0 +1,384 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_ERROR_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_ERROR_HPP
#include "traits.hpp"
#include "single.hpp"
#include "service_traits.hpp"
namespace kgr {
namespace detail {
template<typename T, typename... Args>
struct ServiceError {
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
!is_override_convertible<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"The service injected type cannot be converted to the overriding type. "
"Check if the service is overriding the right service and if types are compatible."
);
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
!is_autocall_valid<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"The service has problem with autocall. Injected services can be invalid, or the service map can be incoplete. "
"Check if all required services are included and if every services are valid."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Service>::value &&
!is_autocall_valid<Service>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Service>::value,
"The service has problem with autocall. Injected services can be invalid, or the service map can be incoplete. "
"Check if all required services are included and if every services are valid."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Service>::value &&
is_service_constructible<Service, Arg, Args...>::value &&
is_autocall_valid<Service>::value &&
!dependency_trait<is_autocall_valid, Service, Arg, Args...>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Service>::value,
"A dependency of this service has problem with autocall. Injected services can be invalid, or the service map can be incoplete. "
"Check if all required services are included and if every services are valid."
);
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
is_service_constructible<Service>::value &&
is_autocall_valid<Service>::value &&
!dependency_trait<is_autocall_valid, Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"A dependency of this service has problem with autocall. Injected services can be invalid, or the service map can be incoplete. "
"Check if all required services are included and if every services are valid."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Service>::value &&
!is_override_convertible<Service>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Service>::value,
"The service injected type cannot be converted to the overriding type. "
"Check if the service is overriding the right service and if types are compatible."
);
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
dependency_trait<is_service, Service>::value &&
!dependency_trait<is_override_convertible, Service>::value &&
is_service_constructible<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"One or more dependencies injected type cannot be converted to one of it's overriding type. "
"Check if that service is overriding the right service and if types are compatible."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Service>::value &&
dependency_trait<is_service, Service, Arg, Args...>::value &&
!dependency_trait<is_override_convertible, Service, Arg, Args...>::value &&
is_service_constructible<Service>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Service>::value,
"One or more dependencies injected type cannot be converted to one of it's overriding type. "
"Check if that service is overriding the right service and if types are compatible."
);
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
is_abstract_service<Service>::value &&
dependency_trait<is_default_service_valid, Service>::value &&
!is_default_service_valid<Service>::value &&
is_default_convertible<Service>::value &&
is_default_overrides_abstract<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"The default implementation of this abstract service is not a well defined service. "
"Please check that types are complete and exposing a valid service definition interface."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Service>::value &&
is_abstract_service<Service>::value &&
dependency_trait<is_default_service_valid, Service, Arg, Args...>::value &&
!is_default_service_valid<Service>::value &&
is_default_convertible<Service>::value &&
is_default_overrides_abstract<Service>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Service>::value,
"The default implementation of this abstract service is not a well defined service. "
"Please check that types are complete and exposing a valid service definition interface."
);
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
is_abstract_service<Service>::value &&
dependency_trait<is_default_service_valid, Service>::value &&
!is_default_service_valid<Service>::value &&
!is_default_convertible<Service>::value &&
is_default_overrides_abstract<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"The service type of the default implementation of this abstract service is not convertible to the abstract service type. "
"Please check that the service type of the default implementation of that abstract service is a compatible type."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Service>::value &&
is_abstract_service<Service>::value &&
dependency_trait<is_default_service_valid, Service, Arg, Args...>::value &&
!is_default_service_valid<Service>::value &&
!is_default_convertible<Service>::value &&
is_default_overrides_abstract<Service>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Service>::value,
"The service type of the default implementation of this abstract service is not convertible to the abstract service type. "
"Please check that the service type of the default implementation of that abstract service is a compatible type."
);
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
is_abstract_service<Service>::value &&
dependency_trait<is_default_service_valid, Service>::value &&
!is_default_service_valid<Service>::value &&
!is_default_overrides_abstract<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"The default implementation of this abstract service is not overriding that abstract service. "
"Ensure that the default implementation really override this abstract service."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Service>::value &&
is_abstract_service<Service>::value &&
dependency_trait<is_default_service_valid, Service, Arg, Args...>::value &&
!is_default_service_valid<Service>::value &&
!is_default_overrides_abstract<Service>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Service>::value,
"The default implementation of this abstract service is not overriding that abstract service. "
"Ensure that the default implementation really override this abstract service."
);
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
!is_abstract_service<Service>::value &&
!is_default_service_valid<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"Non-abstract service cannot have a default implementation."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Service>::value &&
!is_abstract_service<Service>::value &&
!is_default_service_valid<Service>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Service>::value,
"Non-abstract service cannot have a default implementation."
);
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
dependency_trait<is_service, Service>::value &&
is_service_constructible<Service>::value &&
!dependency_trait<is_default_service_valid, Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"The default implementation of a dependency is not valid. "
"Ensure that every default implementation are valid services that override properly thier abstract services."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Service>::value &&
dependency_trait<is_service, Service, Arg, Args...>::value &&
is_service_constructible<Service, Arg, Args...>::value &&
!dependency_trait<is_default_service_valid, Service, Arg, Args...>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Service>::value,
"The default implementation of a dependency is not valid. "
"Ensure that every default implementation are valid services that override properly thier abstract services."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Arg>::value &&
dependency_trait<is_construct_function_callable, Service, Arg, Args...>::value &&
!dependency_trait<is_service_constructible, Service, Arg, Args...>::value &&
is_service_constructible<Service, Arg, Args...>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Arg>::value,
"One or more dependencies are not constructible with it's dependencies as constructor argument. "
"Please check that dependency's constructor and it's dependencies."
);
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
dependency_trait<is_construct_function_callable, Service>::value &&
!dependency_trait<is_service_constructible, Service>::value &&
is_service_constructible<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"One or more dependencies are not constructible with it's dependencies as constructor argument. "
"Please check that dependency's constructor and dependencies."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Arg>::value &&
!dependency_trait<is_construct_function_callable, Service>::value &&
is_construct_function_callable<Service, Arg, Args...>::value &&
is_service_constructible<Service, Arg, Args...>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Arg>::value,
"One or more dependencies construct function cannot be called when calling kgr::Container::service<Dependency>(). "
"Please check that dependency's construct function and ensure that is well formed."
);
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
!dependency_trait<is_construct_function_callable, Service>::value &&
is_construct_function_callable<Service>::value &&
is_service_constructible<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"One or more dependencies construct function cannot be called when calling kgr::Container::service<Dependency>(). "
"Please check that dependency's construct function and ensure that is well formed."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Service>::value &&
!is_service_constructible<Service, Arg, Args...>::value &&
!is_service_constructible<Service>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Arg>::value,
"The service type is not constructible given it's dependencies and passed arguments. "
"Ensure that dependencies are correctly configured and you pass the right set of parameters."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Service>::value &&
!is_service_constructible<Service, Arg, Args...>::value &&
is_service_constructible<Service>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Arg>::value, "The service type is not constructible given passed arguments to kgr::Container::service(...).");
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
is_construct_function_callable<Service>::value &&
!is_service_constructible<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"The service type is not constructible given it's dependencies. "
"Check if dependencies are configured correctly and if the service has the required constructor."
);
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
is_single<Service>::value &&
!is_construct_function_callable<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"The service construct function cannot be called. "
"Check if the construct function is well formed, receive injected arguments first and additional parameters at the end."
);
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
!is_single<Service>::value &&
dependency_trait<is_service, Service>::value &&
!is_construct_function_callable<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"The service construct function cannot be called without arguments, or is not well formed. "
"Check if the construct function is well formed, receive injected arguments first and additional parameters at the end."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Service>::value &&
!is_single<Service>::value &&
dependency_trait<is_service, Service, Arg, Args...>::value &&
!is_construct_function_callable<Service, Arg, Args...>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Service>::value,
"The service construct function cannot be called. "
"Check if the construct function is well formed, receive injected arguments first and additional parameters at the end."
);
}
template<typename Service = T, enable_if_t<
is_service<Service>::value &&
has_any_construct<Service>::value &&
!dependency_trait<is_service, Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value,
"A dependency or one of their dependencies is not a service. Be sure to use the service definition in the list of dependencies of that service."
);
}
template<typename Arg, typename Service = T, enable_if_t<
is_service<Service>::value &&
has_any_construct<Service, Arg, Args...>::value &&
!dependency_trait<is_service, Service, Arg, Args...>::value, int> = 0>
ServiceError(Arg&&) {
static_assert(false_t<Service>::value,
"A dependency or one of their dependencies is not a service. Be sure to use the service definition in the list of dependencies of that service."
);
}
template<typename Service = T, enable_if_t<!is_service<Service>::value && !has_forward<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value, "The type sent to kgr::Container::service(...) is not a service.");
}
template<typename Service = T, enable_if_t<!is_service<Service>::value && has_forward<Service>::value, int> = 0>
ServiceError() {
static_assert(false_t<Service>::value, "The service type must not not contain any virtual function or must be abstract.");
}
};
struct NotInvokableError {
template<typename T = void>
NotInvokableError(...) {
static_assert(false_t<T>::value,
"The function sent is not invokable. Ensure to include all services definitions "
"you need and that received parameters are correct."
);
}
};
} // namespace detail
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_ERROR_HPP

View File

@@ -0,0 +1,135 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_FUNCTION_TRAITS_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_FUNCTION_TRAITS_HPP
#include "meta_list.hpp"
#include "void_t.hpp"
#include <type_traits>
namespace kgr {
namespace detail {
template<typename, typename = void>
struct has_call_operator : std::false_type {};
template<typename T>
struct has_call_operator<T, void_t<decltype(&T::operator())>> : std::true_type {};
template <typename>
struct function_traits_helper {};
template <typename Type, typename R, typename... Args>
struct base_function_traits {
using object_type = Type;
using return_type = R;
using argument_types = meta_list<Args...>;
template<std::size_t n> using argument_type = meta_list_element_t<n, argument_types>;
};
template <typename R, typename... Args>
struct base_non_member_function_traits {
using return_type = R;
using argument_types = meta_list<Args...>;
template<std::size_t n> using argument_type = meta_list_element_t<n, argument_types>;
};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args...) const> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args...)> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args...) const &&> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args...) &&> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args...) const &> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args...) &> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args...) const volatile> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args...) volatile> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args...) const volatile &> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args...) volatile &> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args...) const volatile &&> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args...) volatile &&> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args..., ...) const> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args..., ...)> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args..., ...) const &&> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args..., ...) &&> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args..., ...) const &> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args..., ...) &> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args..., ...) const volatile> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args..., ...) volatile> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args..., ...) const volatile &> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args..., ...) volatile &> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args..., ...) const volatile &&> : base_function_traits<Type, R, Args...> {};
template <typename Type, typename R, typename... Args>
struct function_traits_helper<R(Type::*)(Args..., ...) volatile &&> : base_function_traits<Type, R, Args...> {};
template <typename R, typename... Args>
struct function_traits_helper<R(*)(Args...)> : base_non_member_function_traits<R, Args...> {};
template <typename R, typename... Args>
struct function_traits_helper<R(*)(Args..., ...)> : base_non_member_function_traits<R, Args...> {};
template<typename F, typename = void>
struct function_traits : function_traits_helper<F> {};
template<typename T>
struct function_traits<T, typename std::enable_if<has_call_operator<T>::value>::type> : function_traits_helper<decltype(&T::operator())> {};
template <typename F>
using function_arguments_t = typename function_traits<F>::argument_types;
template <typename F>
using function_result_t = typename function_traits<F>::return_type;
template <typename F>
using object_type_t = typename function_traits<F>::object_type;
template <std::size_t n, typename F>
using function_argument_t = typename function_traits<F>::template argument_type<n>;
} // namespace detail
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_FUNCTION_TRAITS_HPP

View File

@@ -0,0 +1,126 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_INJECTED_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_INJECTED_HPP
#include "utils.hpp"
#include "single.hpp"
#include "traits.hpp"
namespace kgr {
struct Container;
namespace detail {
template<typename T, typename = void>
struct BaseInjectedHelper {
virtual ~BaseInjectedHelper() = default;
kgr::detail::Sink forward();
};
template<typename T>
struct BaseInjectedHelper<T, void_t<ServiceType<T>>> {
virtual ~BaseInjectedHelper() = default;
virtual ServiceType<T> forward() = 0;
};
template<typename T>
struct BaseInjected : BaseInjectedHelper<T> {};
template<typename T>
struct SingleInjected final : BaseInjected<T> {
friend struct kgr::Container;
template<typename... Args, enable_if_t<is_brace_constructible<T, Args...>::value, int> = 0>
explicit SingleInjected(Args&&... args) : _service{std::forward<Args>(args)...} {}
template<typename... Args, enable_if_t<!is_brace_constructible<T, Args...>::value && std::is_constructible<T, Args...>::value, int> = 0>
explicit SingleInjected(Args&&... args) : _service(std::forward<Args>(args)...) {}
ServiceType<T> forward() override {
return _service.forward();
}
private:
T& get() {
return _service;
}
const T& get() const {
return _service;
}
T _service;
};
template<typename Original, typename Service>
struct ServiceOverride final : BaseInjected<Service> {
explicit ServiceOverride(Original& service) : _service{service} {}
ServiceType<Service> forward() override {
return static_cast<ServiceType<Service>>(_service.forward());
}
private:
Original& _service;
};
template<typename T>
struct Injected final {
friend struct kgr::Container;
template<typename... Args, enable_if_t<is_brace_constructible<T, Args...>::value, int> = 0>
explicit Injected(Args&&... args) : _service{std::forward<Args>(args)...} {}
template<typename... Args, enable_if_t<!is_brace_constructible<T, Args...>::value && std::is_constructible<T, Args...>::value, int> = 0>
explicit Injected(Args&&... args) : _service(std::forward<Args>(args)...) {}
ServiceType<T> forward() {
return _service.forward();
}
private:
T& get() {
return _service;
}
const T& get() const {
return _service;
}
T _service;
};
template<typename>
struct original;
template<typename T>
struct original<BaseInjected<T>&> {
using type = T;
};
template<typename T>
struct original<Injected<T>&&> {
using type = T;
};
template<typename T>
using original_t = typename original<T>::type;
template<std::size_t n, typename F>
using injected_argument_t = original_t<function_argument_t<n, F>>;
} // namespace detail
template<typename T>
using Inject = typename std::conditional<!detail::is_service<T>::value || detail::is_single<T>::value,
detail::BaseInjected<T>&,
detail::Injected<T>&&>::type;
template<typename... Args>
std::tuple<detail::remove_rvalue_reference_t<Args>...> inject(Args&&... args) {
return std::tuple<detail::remove_rvalue_reference_t<Args>...>{std::forward<Args>(args)...};
}
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_INJECTED_HPP

View File

@@ -0,0 +1,140 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_INVOKE_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_INVOKE_HPP
#include <type_traits>
#include "meta_list.hpp"
#include "utils.hpp"
#include "traits.hpp"
#include "injected.hpp"
namespace kgr {
struct Container;
template<typename T, T t>
using Method = std::integral_constant<T, t>;
template<typename M, typename... Ps>
struct Invoke : M {
using Parameters = detail::meta_list<Ps...>;
};
template<template<typename> class M, typename... Ts>
struct AutoCall {
using Autocall = detail::meta_list<Ts...>;
template<typename T>
using Map = M<T>;
};
template<typename... Ts>
struct AutoCallNoMap {
using Autocall = detail::meta_list<Ts...>;
};
namespace detail {
template<typename T, typename F>
struct autocall_function {
private:
template<typename U>
static U identity(U);
template<typename U, typename C>
struct get_member_autocall {
using type = std::integral_constant<
decltype(identity(&U::template autocall<C, U::template Map>)),
&U::template autocall<C, U::template Map>
>;
};
template<typename U, typename C, std::size_t... S>
struct get_invoke_autocall {
using type = std::integral_constant<
decltype(identity(&U::template autocall<C, detail::meta_list_element_t<S, typename C::Parameters>...>)),
&U::template autocall<C, detail::meta_list_element_t<S, typename C::Parameters>...>
>;
};
template<typename U, typename C, std::size_t... S>
static get_invoke_autocall<U, C, S...> test_helper(seq<S...>);
template<typename U, typename C, enable_if_t<is_invoke_call<C>::value, int> = 0>
static decltype(test_helper<U, C>(tuple_seq<typename C::Parameters>{})) test();
template<typename U, typename C, enable_if_t<is_member_autocall<U, C>::value && !is_invoke_call<C>::value, int> = 0>
static get_member_autocall<U, C> test();
using inner_type = decltype(test<T, F>());
public:
using type = typename inner_type::type;
};
template<typename, typename, typename = void>
struct autocall_arguments;
template<typename T, typename F>
struct autocall_arguments<T, F, enable_if_t<is_invoke_call<F>::value>> {
using type = typename F::Parameters;
};
template<typename T, typename F>
struct autocall_arguments<T, F, enable_if_t<is_member_autocall<T, F>::value && !is_invoke_call<F>::value>> {
private:
template<template<typename> class Map>
struct mapped_type {
template<typename U>
using map = service_map_t<Map, U>;
};
public:
using type = meta_list_transform_t<function_arguments_t<typename F::value_type>, mapped_type<T::template Map>::template map>;
};
template<typename T, typename F>
using autocall_function_t = typename autocall_function<T, F>::type;
template<typename T, std::size_t I>
using autocall_nth_function = detail::autocall_function_t<T, detail::meta_list_element_t<I, typename T::Autocall>>;
template<typename T, std::size_t I>
using autocall_nth_function_t = typename detail::autocall_function_t<T, detail::meta_list_element_t<I, typename T::Autocall>>::value_type;
template<typename T, typename F>
using autocall_arguments_t = typename autocall_arguments<T, F>::type;
template<typename T, typename F>
using is_valid_autocall_function = std::integral_constant<bool,
is_invoke_call<F>::value || is_member_autocall<T, F>::value
>;
template<typename, typename = void>
struct map_entry {};
template<typename P>
struct map_entry<P, enable_if_t<is_service<decltype(service_map(std::declval<P>(), std::declval<kgr::Map<> >()))>::value>> {
using Service = decltype(service_map(std::declval<P>(), std::declval<kgr::Map<> >()));
};
template<typename P>
struct map_entry<P, enable_if_t<is_service<decltype(service_map(std::declval<P>()))>::value>> {
using Service = decltype(service_map(std::declval<P>()));
};
template<typename P>
struct map_entry<P, enable_if_t<is_map<decltype(service_map(std::declval<P>(), std::declval<kgr::Map<> >()))>::value>> :
decltype(service_map(std::declval<P>(), std::declval<kgr::Map<> >())) {};
template<typename P>
struct map_entry<P, enable_if_t<is_map<decltype(service_map(std::declval<P>()))>::value>> :
decltype(service_map(std::declval<P>())) {};
} // namespace detail
template<typename P>
struct AdlMap : detail::map_entry<P> {};
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_INVOKE_HPP

View File

@@ -0,0 +1,50 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_LAZY_BASE_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_LAZY_BASE_HPP
#include "../container.hpp"
#include "lazy_storage.hpp"
namespace kgr {
namespace detail {
template<typename CRTP, typename T>
struct LazyBase {
private:
using ref = typename std::remove_reference<ServiceType<T>>::type&;
using rref = typename std::remove_reference<ServiceType<T>>::type&&;
using ptr = decay_t<ServiceType<T>>*;
static constexpr bool nothrow_get() {
return noexcept(std::declval<Container>().service<T>()) &&
noexcept(std::declval<LazyStorage<ServiceType<T>>>().construct(std::declval<ServiceType<T>>()));
}
public:
ref get() noexcept(nothrow_get()) {
if (!_service) {
_service.construct(static_cast<CRTP*>(this)->container().template service<T>());
}
return _service.value();
}
ref operator*() & noexcept(nothrow_get()) {
return get();
}
ptr operator->() noexcept(nothrow_get()) {
return &get();
}
rref operator*() && noexcept(nothrow_get()) {
return std::move(get());
}
private:
LazyStorage<ServiceType<T>> _service;
};
} // namespace detail
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_LAZY_BASE_HPP

View File

@@ -0,0 +1,334 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_LAZY_STORAGE_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_LAZY_STORAGE_HPP
#include <type_traits>
#include "traits.hpp"
namespace kgr {
namespace detail {
template<typename T>
using lazy_stored_type = typename std::conditional<
std::is_lvalue_reference<T>::value,
typename std::add_pointer<typename std::remove_reference<T>::type>::type,
typename std::decay<T>::type
>::type;
template<typename CRTP, typename T, template<typename, typename, typename = void> class... Bases>
struct LazyCrtpHelper : Bases<CRTP, T>... {};
template<typename CRTP, typename T, typename = void>
struct LazyCopyConstruct {
LazyCopyConstruct() = default;
LazyCopyConstruct(const LazyCopyConstruct&) = delete;
LazyCopyConstruct(LazyCopyConstruct&&) = default;
LazyCopyConstruct& operator=(const LazyCopyConstruct&) = default;
LazyCopyConstruct& operator=(LazyCopyConstruct&&) = default;
};
template<typename CRTP, typename T, typename = void>
struct LazyMoveAssign {
LazyMoveAssign() = default;
LazyMoveAssign(const LazyMoveAssign&) = default;
LazyMoveAssign(LazyMoveAssign&&) = default;
LazyMoveAssign& operator=(const LazyMoveAssign&) = default;
LazyMoveAssign& operator=(LazyMoveAssign&&) = delete;
};
template<typename CRTP, typename T, typename = void>
struct LazyCopyAssign {
LazyCopyAssign() = default;
LazyCopyAssign(const LazyCopyAssign&) = default;
LazyCopyAssign(LazyCopyAssign&&) = default;
LazyCopyAssign& operator=(const LazyCopyAssign&) = delete;
LazyCopyAssign& operator=(LazyCopyAssign&&) = default;
};
template<typename CRTP, typename T, typename = void>
struct LazyMoveConstruct {
LazyMoveConstruct() = default;
LazyMoveConstruct(const LazyMoveConstruct&) = default;
LazyMoveConstruct(LazyMoveConstruct&&) = delete;
LazyMoveConstruct& operator=(const LazyMoveConstruct&) = default;
LazyMoveConstruct& operator=(LazyMoveConstruct&&) = default;
};
template<typename CRTP, typename T>
struct LazyCopyConstruct<CRTP, T, enable_if_t<std::is_copy_constructible<T>::value && !std::is_trivially_copy_constructible<T>::value>> {
LazyCopyConstruct(const LazyCopyConstruct& other) noexcept(std::is_nothrow_copy_constructible<T>::value){
auto&& o = static_cast<const CRTP&>(other);
if (o) {
static_cast<CRTP&>(*this).emplace(o.data());
}
}
LazyCopyConstruct() = default;
LazyCopyConstruct& operator=(const LazyCopyConstruct&) = default;
LazyCopyConstruct(LazyCopyConstruct&&) = default;
LazyCopyConstruct& operator=(LazyCopyConstruct&&) = default;
};
template<typename CRTP, typename T>
struct LazyCopyConstruct<CRTP, T, enable_if_t<std::is_trivially_copy_assignable<T>::value>> {};
template<typename CRTP, typename T>
struct LazyCopyAssign<CRTP, T, enable_if_t<
std::is_copy_assignable<T>::value && std::is_copy_constructible<T>::value &&
!(std::is_trivially_copy_assignable<T>::value && std::is_trivially_copy_constructible<T>::value && std::is_trivially_destructible<T>::value)
>> {
LazyCopyAssign& operator=(const LazyCopyAssign& other)
noexcept(
std::is_nothrow_copy_assignable<T>::value &&
std::is_nothrow_copy_constructible<T>::value &&
std::is_nothrow_destructible<T>::value
) {
auto&& o = static_cast<const CRTP&>(other);
auto&& self = static_cast<CRTP&>(*this);
if (o) {
self.assign(o.data());
} else {
self.destroy();
}
return *this;
}
LazyCopyAssign() = default;
LazyCopyAssign(const LazyCopyAssign&) = default;
LazyCopyAssign(LazyCopyAssign&&) = default;
LazyCopyAssign& operator=(LazyCopyAssign&&) = default;
};
template<typename CRTP, typename T>
struct LazyCopyAssign<CRTP, T, enable_if_t<
std::is_trivially_copy_assignable<T>::value &&
std::is_trivially_copy_constructible<T>::value &&
std::is_trivially_destructible<T>::value
>> {};
template<typename CRTP, typename T>
struct LazyMoveConstruct<CRTP, T, enable_if_t<std::is_move_constructible<T>::value && !std::is_trivially_move_constructible<T>::value>> {
LazyMoveConstruct(LazyMoveConstruct&& other) noexcept(std::is_nothrow_move_constructible<T>::value) {
auto&& o = static_cast<CRTP&>(other);
if (o) {
static_cast<CRTP&>(*this).emplace(std::move(o.data()));
}
}
LazyMoveConstruct() = default;
LazyMoveConstruct(const LazyMoveConstruct&) = default;
LazyMoveConstruct& operator=(LazyMoveConstruct&&) = default;
LazyMoveConstruct& operator=(const LazyMoveConstruct&) = default;
};
template<typename CRTP, typename T>
struct LazyMoveConstruct<CRTP, T, enable_if_t<std::is_trivially_move_constructible<T>::value>> {};
template<typename CRTP, typename T>
struct LazyMoveAssign<CRTP, T, enable_if_t<
std::is_move_assignable<T>::value && std::is_move_constructible<T>::value &&
!(std::is_trivially_move_assignable<T>::value && std::is_trivially_move_constructible<T>::value && std::is_trivially_destructible<T>::value)
>> {
LazyMoveAssign& operator=(LazyMoveAssign&& other)
noexcept(
std::is_nothrow_move_assignable<T>::value &&
std::is_nothrow_move_constructible<T>::value &&
std::is_nothrow_destructible<T>::value
) {
auto&& o = static_cast<CRTP&>(other);
auto&& self = static_cast<CRTP&>(*this);
if (o) {
self.assign(std::move(o.data()));
} else {
self.destroy();
}
return *this;
}
LazyMoveAssign() = default;
LazyMoveAssign(const LazyMoveAssign&) = default;
LazyMoveAssign(LazyMoveAssign&&) = default;
LazyMoveAssign& operator=(const LazyMoveAssign&) = default;
};
template<typename CRTP, typename T>
struct LazyMoveAssign<CRTP, T, enable_if_t<
std::is_trivially_move_assignable<T>::value &&
std::is_trivially_move_constructible<T>::value &&
std::is_trivially_destructible<T>::value
>> {};
template<typename CRTP, typename T, typename = void>
struct LazyDestruction {
~LazyDestruction() noexcept(std::is_nothrow_destructible<T>::value) {
destructor();
}
protected:
void destructor() noexcept(std::is_nothrow_destructible<T>::value) {
auto&& self = static_cast<CRTP&>(*this);
using DestructingType = typename CRTP::type;
if (self) {
self.data().~DestructingType();
}
}
};
template<typename CRTP, typename T>
struct LazyDestruction<CRTP, T, enable_if_t<std::is_trivially_destructible<T>::value>> {
protected:
void destructor() noexcept {}
};
template<typename CRTP, typename T>
struct LazyStorageBase :
LazyCrtpHelper<
CRTP, lazy_stored_type<T>,
LazyCopyConstruct, LazyCopyAssign, LazyMoveAssign, LazyMoveConstruct, LazyDestruction> {
using type = lazy_stored_type<T>;
LazyStorageBase() = default;
LazyStorageBase& operator=(LazyStorageBase&&) = default;
LazyStorageBase& operator=(const LazyStorageBase&) = default;
LazyStorageBase(LazyStorageBase&&) = default;
LazyStorageBase(const LazyStorageBase&) = default;
~LazyStorageBase() = default;
friend struct LazyMoveAssign<CRTP, lazy_stored_type<T>>;
template<typename Arg>
void assign(Arg&& arg) noexcept(std::is_nothrow_constructible<type, Arg>::value && std::is_nothrow_assignable<type, Arg>::value) {
auto&& self = static_cast<CRTP&>(*this);
if (self) {
data() = std::forward<Arg>(arg);
} else {
emplace(std::forward<Arg>(arg));
}
}
template<typename... Args>
void emplace(Args&&... args) noexcept(std::is_nothrow_constructible<type, Args...>::value) {
static_cast<CRTP&>(*this).reset();
new (&_data) type(std::forward<Args>(args)...);
}
protected:
using LazyDestruction<CRTP, lazy_stored_type<T>>::destructor;
type& data() noexcept {
return *reinterpret_cast<type*>(&_data);
}
const type& data() const noexcept {
return *reinterpret_cast<const type*>(&_data);
}
typename std::aligned_storage<sizeof(type), alignof(type)>::type _data;
};
template<typename T>
struct LazyStorage : LazyStorageBase<LazyStorage<T>, T> {
private:
using Base = LazyStorageBase<LazyStorage<T>, T>;
using Base::_data;
friend struct LazyStorageBase<LazyStorage<T>, T>;
public:
using Base::data;
using Base::emplace;
using typename Base::type;
LazyStorage() = default;
LazyStorage& operator=(LazyStorage&&) = default;
LazyStorage& operator=(const LazyStorage&) = default;
LazyStorage(LazyStorage&&) = default;
LazyStorage(const LazyStorage&) = default;
~LazyStorage() = default;
explicit operator bool() const noexcept {
return _initialized;
}
void construct(T&& value) noexcept(std::is_nothrow_constructible<type, T&&>::value) {
emplace(std::move(value));
}
T& value() noexcept {
return data();
}
void destroy() noexcept(std::is_nothrow_destructible<type>::value) {
_initialized = false;
Base::destructor();
}
private:
void reset() noexcept(std::is_nothrow_destructible<type>::value) {
Base::destructor();
_initialized = true;
}
bool _initialized = false;
};
template<typename T>
struct LazyStorage<T&> : LazyStorageBase<LazyStorage<T&>, T&> {
private:
using Base = LazyStorageBase<LazyStorage<T&>, T&>;
using Base::_data;
friend struct LazyStorageBase<LazyStorage<T&>, T&>;
public:
using Base::data;
using Base::emplace;
using typename Base::type;
LazyStorage() {
emplace(nullptr);
}
LazyStorage& operator=(LazyStorage&&) = default;
LazyStorage& operator=(const LazyStorage&) = default;
LazyStorage(LazyStorage&&) = default;
LazyStorage(const LazyStorage&) = default;
~LazyStorage() = default;
explicit operator bool() const noexcept {
return data();
}
void construct(T& value) noexcept(std::is_nothrow_constructible<type, type*>::value) {
emplace(&value);
}
T& value() noexcept {
return *data();
}
void destroy() noexcept {
data() = nullptr;
}
private:
void reset() noexcept {}
};
} // namespace detail
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_LAZY_STORAGE_HPP

View File

@@ -0,0 +1,70 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_META_LIST_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_META_LIST_HPP
#include <type_traits>
namespace kgr {
namespace detail {
template<typename... Types>
struct meta_list {};
template<std::size_t, typename>
struct meta_list_element;
template<std::size_t I>
struct meta_list_element<I, meta_list<>> {};
template<typename Head, typename... Tail>
struct meta_list_element<0, meta_list<Head, Tail...>> {
using type = Head;
};
template<std::size_t I, typename Head, typename... Tail>
struct meta_list_element<I, meta_list<Head, Tail...>> : meta_list_element<I - 1, meta_list<Tail...>> {};
template <typename, typename>
struct meta_list_contains;
template <typename T>
struct meta_list_contains<T, meta_list<>> : std::false_type {};
template <typename T, typename Head, typename... Tail>
struct meta_list_contains<T, meta_list<Head, Tail...>> : meta_list_contains<T, meta_list<Tail...>> {};
template <typename T, typename... Tail>
struct meta_list_contains<T, meta_list<T, Tail...>> : std::true_type {};
template <typename>
struct meta_list_size;
template<typename, template<typename...> class>
struct meta_list_transform;
template<typename... Types, template<typename> class F>
struct meta_list_transform<meta_list<Types...>, F> {
using type = meta_list<F<Types>...>;
};
template<typename List, template<typename...> class F>
using meta_list_transform_t = typename meta_list_transform<List, F>::type;
template <typename... Types>
struct meta_list_size<meta_list<Types...>> {
static constexpr std::size_t value = sizeof...(Types);
};
template<std::size_t I, typename List>
using meta_list_element_t = typename meta_list_element<I, List>::type;
template<typename>
struct meta_list_empty;
template<typename... Types>
struct meta_list_empty<meta_list<Types...>> : std::integral_constant<bool, sizeof...(Types) == 0> {};
} // namespace detail
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_META_LIST_HPP

View File

@@ -0,0 +1,704 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_TRAITS_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_TRAITS_HPP
#include "traits.hpp"
#include "single.hpp"
#include "injected.hpp"
#include "invoke.hpp"
#include "container_service.hpp"
#ifdef _MSC_VER
#ifndef __clang__
#define KGR_KANGARU_MSVC_NO_AUTOCALL_MAP_CHECK
#endif // !__clang__
#endif // _MSC_VER
#if _MSC_VER == 1900
#ifndef __clang__
#define KGR_KANGARU_MSVC_NO_DEPENDENT_TEMPLATE_KEYWORD
#endif // !__clang__
#endif // _MSC_VER
namespace kgr {
namespace detail {
template<typename T>
using is_container_service = std::is_base_of<ContainerServiceTag, T>;
template<typename F, typename... Args>
struct is_construct_invokable_helper {
private:
template<typename...>
static std::false_type test_helper(...);
template<typename C, typename... As, std::size_t... S, int_t<
decltype(std::declval<C>()(std::declval<function_argument_t<S, C>>()..., std::declval<As>()...)),
enable_if_t<is_service<injected_argument_t<S, C>>::value>...> = 0>
static std::true_type test_helper(seq<S...>);
template<typename C, typename... As>
static decltype(test_helper<C, As...>(tuple_seq_minus<function_arguments_t<C>, sizeof...(As)>{})) test(int);
template<typename...>
static std::false_type test(...);
public:
using type = decltype(test<F, Args...>(0));
};
template<typename F, typename... Args>
using is_construct_invokable = typename is_construct_invokable_helper<F, Args...>::type;
template<typename, typename, typename, typename = void>
struct has_callable_template_construct : std::false_type {};
template<typename T, typename... TArgs, typename... Args>
struct has_callable_template_construct<
T, meta_list<TArgs...>, meta_list<Args...>,
enable_if_t<is_construct_invokable<decltype(&T::template construct<TArgs...>), Args...>::value>
> : std::true_type {};
template<typename, typename, typename, typename = void>
struct get_template_construct_helper;
template<typename T, typename... Args>
struct get_template_construct_helper<
T, meta_list<>, meta_list<Args...>,
enable_if_t<!has_callable_template_construct<T, meta_list<>, meta_list<Args...>>::value>
> {};
template<typename T, typename Head, typename... Tail, typename... Args>
struct get_template_construct_helper<
T, meta_list<Head, Tail...>, meta_list<Args...>,
enable_if_t<!has_callable_template_construct<T, meta_list<Head, Tail...>, meta_list<Args...>>::value>
> : get_template_construct_helper<T, meta_list<Tail...>, meta_list<Args...>> {};
template<typename T, typename... TArgs, typename... Args>
struct get_template_construct_helper<
T, meta_list<TArgs...>, meta_list<Args...>,
enable_if_t<has_callable_template_construct<T, meta_list<TArgs...>, meta_list<Args...>>::value>
> : std::integral_constant<decltype(&T::template construct<TArgs...>), &T::template construct<TArgs...>> {};
template<typename T, typename... Args>
using get_template_construct = get_template_construct_helper<T, meta_list<Args...>, meta_list<Args...>>;
template<typename, typename, typename = void>
struct template_construct_exist : std::false_type {};
template<typename T, typename... Args>
struct template_construct_exist<T, meta_list<Args...>, void_t<decltype(&T::template construct<Args...>)>> : std::true_type {};
template<typename, typename, typename = void>
struct get_any_template_construct_helper;
template<typename T, typename Head, typename... Tail>
struct get_any_template_construct_helper<
T, meta_list<Head, Tail...>,
enable_if_t<!template_construct_exist<T, meta_list<Head, Tail...>>::value>
> : get_any_template_construct_helper<T, meta_list<Tail...>> {};
template<typename T, typename... Args>
struct get_any_template_construct_helper<
T, meta_list<Args...>,
enable_if_t<template_construct_exist<T, meta_list<Args...>>::value>
> : std::integral_constant<decltype(&T::template construct<Args...>), &T::template construct<Args...>> {};
template<typename T, typename... Args>
using get_any_template_construct = get_any_template_construct_helper<T, meta_list<Args...>>;
template<typename, typename, typename = void>
struct has_any_template_construct_helper : std::false_type {};
template<typename T, typename... Args>
struct has_any_template_construct_helper<T, meta_list<Args...>, void_t<typename get_any_template_construct<T, Args...>::value_type>> : std::true_type {};
template<typename T, typename... Args>
using has_any_template_construct = has_any_template_construct_helper<T, meta_list<Args...>>;
template<typename, typename, typename = void>
struct has_template_construct_helper : std::false_type {};
template<typename T, typename... Args>
struct has_template_construct_helper<T, meta_list<Args...>, void_t<typename get_template_construct<T, Args...>::value_type>> : std::true_type {};
template<typename T, typename... Args>
using has_template_construct = has_template_construct_helper<T, meta_list<Args...>>;
template<bool, typename T, typename... Args>
struct construct_function_helper {
private:
template<typename U>
struct get_construct {
using type = std::integral_constant<decltype(&U::construct), &U::construct>;
};
template<typename U, typename... As, enable_if_t<
has_construct<U>::value &&
!has_template_construct<U, As...>::value, int> = 0>
static get_construct<U> test();
template<typename U, typename... As, enable_if_t<has_template_construct<U, As...>::value, int> = 0>
static get_template_construct<U, As...> test();
using inner_type = decltype(test<T, Args...>());
public:
using type = typename inner_type::type;
};
template<typename T, typename... Args>
struct construct_function_helper<false, T, Args...> {};
template<typename T, typename... Args>
struct has_any_construct {
constexpr static bool value = has_any_template_construct<T, Args...>::value || has_construct<T>::value;
};
template<typename T, typename... Args>
using has_valid_construct = std::integral_constant<bool, has_template_construct<T, Args...>::value || has_construct<T>::value>;
template<typename T, typename... Args>
using construct_function = typename construct_function_helper<has_valid_construct<T, Args...>::value, T, Args...>::type;
template<typename T, typename... Args>
using construct_function_t = typename construct_function<T, Args...>::value_type;
template<typename T, typename... Args>
using construct_result_seq = tuple_seq<function_result_t<construct_function_t<T, Args...>>>;
template<typename, typename, typename = void>
struct is_construct_function_callable_helper : std::false_type {};
template<typename T, typename... Args>
struct is_construct_function_callable_helper<T, meta_list<Args...>, enable_if_t<is_construct_invokable<construct_function_t<T, Args...>, Args...>::value>> : std::true_type {};
template<typename T, typename... Args>
struct is_construct_function_callable_helper<T, meta_list<Args...>, enable_if_t<is_container_service<T>::value || is_abstract_service<T>::value>> : std::true_type {};
template<typename T, typename... Args>
using is_construct_function_callable = is_construct_function_callable_helper<T, meta_list<Args...>>;
template<template<typename...> class Trait, typename T, typename... Args>
struct dependency_trait_helper {
template<typename U, std::size_t I, typename... As>
struct expand {
using type = Trait<injected_argument_t<I, construct_function_t<U, As...>>>;
};
template<typename U, typename... As, std::size_t... S, int_t<
enable_if_t<dependency_trait_helper<Trait, injected_argument_t<S, construct_function_t<U, As...>>>::type::value>...,
enable_if_t<expand<U, S, As...>::type::value>...> = 0>
static std::true_type test(seq<S...>);
template<typename U, typename... As, enable_if_t<!has_any_construct<U, As... >::value, int> = 0>
static std::true_type test_helper(int);
template<typename...>
static std::false_type test(...);
template<typename...>
static std::false_type test_helper(...);
template<typename U, typename... As, enable_if_t<has_any_construct<U, As...>::value, int> = 0, int_t<construct_function_t<U, As...>> = 0>
static decltype(test<U, As...>(tuple_seq_minus<function_arguments_t<construct_function_t<U, As...>>, sizeof...(As)>{})) test_helper(int);
public:
using type = decltype(test_helper<T, Args...>(0));
};
template<template<typename...> class Trait, typename T, typename... Args>
using dependency_trait = typename dependency_trait_helper<Trait, T, Args...>::type;
template<typename T, typename... Args>
struct is_service_constructible_helper {
private:
template<typename U, typename... As, std::size_t F, std::size_t... S>
static is_service_instantiable<T,
tuple_element_t<F, function_result_t<construct_function_t<U, As...>>>,
tuple_element_t<S, function_result_t<construct_function_t<U, As...>>>...> test(seq<F, S...>);
// This overload is needed for msvc.
// Or else it will try to call the one just above with a 0 as S for strange reason.
template<typename U, typename... As, int_t<construct_function_t<U, As...>> = 0>
static is_service_instantiable<T> test(seq<>);
template<typename U, typename... As, enable_if_t<!has_any_construct<U, As... >::value, int> = 0>
static std::true_type test_helper(int);
template<typename...>
static std::false_type test(...);
template<typename...>
static std::false_type test_helper(...);
// The enable if is required here or else the function call will be ambiguous on visual studio.
template<typename U, typename... As, enable_if_t<has_any_construct<U, As... >::value, int> = 0>
static decltype(test<U, As...>(tuple_seq<function_result_t<construct_function_t<U, As...>>>{})) test_helper(int);
public:
using type = decltype(test_helper<T, Args...>(0));
};
template<typename T, typename... Args>
using is_service_constructible = typename is_service_constructible_helper<T, Args...>::type;
template<typename T>
struct is_override_service_helper {
private:
template<typename...>
static std::false_type test(...);
template<typename U, std::size_t... S, int_t<enable_if_t<is_service<meta_list_element_t<S, parent_types<U>>>::value>...> = 0>
static std::true_type test(seq<S...>);
public:
using type = decltype(test<T>(tuple_seq<parent_types<T>>{}));
};
template<typename T>
using is_override_service = typename is_override_service_helper<T>::type;
template<typename T>
struct is_override_convertible_helper {
private:
// This is a workaround for msvc. Expansion in very complex expression
// leaves the compiler without clues about what's going on.
template<std::size_t I, typename U>
struct expander {
using type = is_explicitly_convertible<ServiceType<U>, ServiceType<meta_list_element_t<I, parent_types<U>>>>;
};
template<typename...>
static std::false_type test(...);
template<typename...>
static std::false_type test_helper(...);
template<typename U, std::size_t... S, int_t<enable_if_t<expander<S, U>::type::value>...> = 0>
static std::true_type test_helper(seq<S...>);
template<typename U, std::size_t... S, int_t<enable_if_t<is_service<meta_list_element_t<S, parent_types<U>>>::value>...> = 0>
static decltype(test_helper<U>(seq<S...>{})) test(seq<S...>);
public:
using type = decltype(test<T>(tuple_seq<parent_types<T>>{}));
};
template<typename T>
using is_override_convertible = typename is_override_convertible_helper<T>::type;
template<typename T>
struct is_override_services_helper {
private:
// This is a workaround for msvc. Expansion in very complex expression
// leaves the compiler without clues about what's going on.
template<std::size_t I, typename U>
struct expander {
using type = is_service<meta_list_element_t<I, parent_types<U>>>;
};
template<typename...>
static std::false_type test(...);
template<typename...>
static std::false_type test_helper(...);
template<typename U, std::size_t... S, int_t<enable_if_t<expander<S, U>::type::value>...> = 0>
static std::true_type test_helper(seq<S...>);
template<typename U, std::size_t... S, int_t<enable_if_t<is_service<meta_list_element_t<S, parent_types<U>>>::value>...> = 0>
static decltype(test_helper<U>(seq<S...>{})) test(seq<S...>);
public:
using type = decltype(test<T>(tuple_seq<parent_types<T>>{}));
};
template<typename T>
using is_override_services = typename is_override_services_helper<T>::type;
template<typename T, typename... Args>
using is_single_no_args = std::integral_constant<bool,
!is_single<T>::value || meta_list_empty<meta_list<Args...>>::value
>;
template<typename T>
struct is_default_overrides_abstract_helper {
private:
template<typename>
static std::false_type test(...);
template<typename U, enable_if_t<has_default<U>::value && is_abstract_service<U>::value, int> = 0>
static is_overriden_by<U, default_type<U>> test(int);
template<typename U, enable_if_t<!has_default<U>::value, int> = 0>
static std::true_type test(int);
public:
using type = decltype(test<T>(0));
};
template<typename T>
using is_default_overrides_abstract = typename is_default_overrides_abstract_helper<T>::type;
template<typename T>
struct is_default_convertible_helper {
private:
template<typename>
static std::false_type test(...);
template<typename U, enable_if_t<has_default<U>::value, int> = 0>
static is_explicitly_convertible<ServiceType<default_type<U>>, ServiceType<U>> test(int);
template<typename U, enable_if_t<!has_default<U>::value, int> = 0>
static std::true_type test(int);
public:
using type = decltype(test<T>(0));
};
template<typename T>
using is_default_convertible = typename is_default_convertible_helper<T>::type;
template<typename T>
using is_default_service_valid = std::integral_constant<bool,
(is_abstract_service<T>::value || !has_default<T>::value) &&
is_default_overrides_abstract<T>::value &&
is_default_convertible<T>::value
>;
template<typename T, typename... Args>
using service_check = std::integral_constant<bool,
is_service<T>::value &&
is_service_constructible<T, Args...>::value &&
is_construct_function_callable<T, Args...>::value &&
is_default_service_valid<T>::value &&
is_override_convertible<T>::value &&
is_override_services<T>::value
>;
template<typename T, typename... Args>
using dependency_check = std::integral_constant<bool,
dependency_trait<is_service, T, Args...>::value &&
dependency_trait<is_service_constructible, T, Args...>::value &&
dependency_trait<is_construct_function_callable, T, Args...>::value &&
dependency_trait<is_default_service_valid, T, Args...>::value &&
dependency_trait<is_override_convertible, T, Args...>::value &&
dependency_trait<is_override_services, T, Args...>::value
>;
template<template<typename> class Map, typename T, typename P, typename... Args>
struct is_pointer_invokable_helper {
private:
template<typename U, typename V, typename... As, std::size_t... S, int_t<
decltype((std::declval<U>().*std::declval<V>())(std::declval<ServiceType<service_map_t<Map, function_argument_t<S, V>>>>()..., std::declval<As>()...))> = 0>
static std::true_type test(seq<S...>);
template<typename...>
static std::false_type test(...);
public:
using type = decltype(test<T, P, Args...>(tuple_seq_minus<function_arguments_t<P>, sizeof...(Args)>{}));
};
template<template<typename> class Map, typename T, typename P, typename... Args>
struct is_pointer_invokable : is_pointer_invokable_helper<Map, T, P, Args...>::type {};
template<template<typename> class, typename, typename, typename, typename = void>
struct has_callable_template_call : std::false_type {};
template<template<typename> class Map, typename T, typename... TArgs, typename... Args>
struct has_callable_template_call<
Map, T, meta_list<TArgs...>, meta_list<Args...>,
enable_if_t<is_pointer_invokable<Map, T,
#ifdef KGR_KANGARU_MSVC_NO_DEPENDENT_TEMPLATE_KEYWORD
decltype(exact(&T::operator()<TArgs...>)),
#else
decltype(exact(&T::template operator()<TArgs...>)),
#endif // KGR_KANGARU_MSVC_NO_DEPENDENT_TEMPLATE_KEYWORD
Args...>::value>
> : std::true_type {};
template<template<typename> class, typename, typename, typename, typename = void>
struct get_template_call_helper;
template<template<typename> class Map, typename T, typename... Args>
struct get_template_call_helper<
Map, T, meta_list<>, meta_list<Args...>,
enable_if_t<!has_callable_template_call<Map, T, meta_list<>, meta_list<Args...>>::value>
> {};
template<template<typename> class Map, typename T, typename Head, typename... Tail, typename... Args>
struct get_template_call_helper<
Map, T, meta_list<Head, Tail...>, meta_list<Args...>,
enable_if_t<!has_callable_template_call<Map, T, meta_list<Head, Tail...>, meta_list<Args...>>::value>
> : get_template_call_helper<Map, T, meta_list<Tail...>, meta_list<Args...>> {};
template<template<typename> class Map, typename T, typename... TArgs, typename... Args>
struct get_template_call_helper<
Map, T, meta_list<TArgs...>, meta_list<Args...>,
enable_if_t<has_callable_template_call<Map, T, meta_list<TArgs...>, meta_list<Args...>>::value>
> {
#ifdef KGR_KANGARU_MSVC_NO_DEPENDENT_TEMPLATE_KEYWORD
using type = decltype(exact(&T::operator()<TArgs...>));
#else
using type = decltype(exact(&T::template operator()<TArgs...>));
#endif // KGR_KANGARU_MSVC_NO_DEPENDENT_TEMPLATE_KEYWORD
};
template<template<typename> class Map, typename T, typename... Args>
using get_template_call = get_template_call_helper<Map, T, meta_list<Args...>, meta_list<Args...>>;
template<template<typename> class, typename, typename, typename = void>
struct has_template_call_operator : std::false_type {};
template<template<typename> class Map, typename T, typename... Args>
struct has_template_call_operator<Map, T, meta_list<Args...>, void_t<typename get_template_call<Map, T, Args...>::type>> : std::true_type {};
template<template<typename> class Map, typename, typename, typename = void>
struct invoke_function_helper {};
template<template<typename> class Map, typename T, typename... Args>
struct invoke_function_helper<Map, T, meta_list<Args...>, enable_if_t<has_call_operator<T>::value && !has_template_call_operator<Map, T, meta_list<Args...>>::value>> {
using type = decltype(&T::operator());
using return_type = function_result_t<type>;
using argument_types = function_arguments_t<type>;
template<std::size_t n> using argument_type = meta_list_element_t<n, argument_types>;
};
template<template<typename> class Map, typename T, typename... Args>
struct invoke_function_helper<Map, T, meta_list<Args...>, void_t<function_result_t<T>, enable_if_t<!has_call_operator<T>::value && !has_template_call_operator<Map, T, meta_list<Args...>>::value>>> {
using type = T;
using return_type = function_result_t<type>;
using argument_types = function_arguments_t<type>;
template<std::size_t n> using argument_type = meta_list_element_t<n, argument_types>;
};
template<template<typename> class Map, typename T, typename... Args>
struct invoke_function_helper<
Map, T, meta_list<Args...>,
enable_if_t<!has_call_operator<T>::value && has_template_call_operator<Map, T, meta_list<Args...>>::value>
> {
using type = typename get_template_call<Map, T, Args...>::type;
using return_type = function_result_t<type>;
using argument_types = function_arguments_t<type>;
template<std::size_t n> using argument_type = meta_list_element_t<n, argument_types>;
};
template<template<typename> class Map, typename T, typename... Args>
struct invoke_function : invoke_function_helper<Map, T, meta_list<Args...>> {};
template<template<typename> class Map, typename T, typename... Args>
using invoke_function_t = typename invoke_function<Map, T, Args...>::type;
template<template<typename> class Map, typename T, typename... Args>
using invoke_function_arguments_t = typename invoke_function<Map, T, Args...>::argument_types;
template<std::size_t n, template<typename> class Map, typename T, typename... Args>
using invoke_function_argument_t = typename invoke_function<Map, T, Args...>::template argument_type<n>;
template<template<typename> class Map, typename T, typename... Args>
using invoke_function_result_t = typename invoke_function<Map, T, Args...>::return_type;
template<template<typename> class Map, typename T, typename... Args>
struct is_invokable_helper {
private:
template<std::size_t I, typename U>
struct expand {
using type = invoke_function_argument_t<I, Map, U, Args...>;
};
// This sub trait is for visual studio
// The constraint can be simplified because every argument are simple template argument
template<typename U, typename... As>
struct call_test {
private:
template<typename...>
static std::false_type test(...);
template<typename V, typename... A2s, int_t<decltype(std::declval<V>()(std::declval<A2s>()...))> = 0>
static std::true_type test(int);
using type = decltype(test<U, As...>(0));
public:
static constexpr bool value = type::value;
};
template<typename U>
using map_t = service_map_t<Map, U>;
template<typename U, typename... As, std::size_t... S, enable_if_t<call_test<U, ServiceType<map_t<typename expand<S, U>::type>>..., As...>::value, int> = 0>
static std::true_type test_helper(seq<S...>);
template<typename...>
static std::false_type test_helper(...);
template<typename U, typename... As>
static decltype(test_helper<U, As...>(tuple_seq_minus<invoke_function_arguments_t<Map, U, As...>, sizeof...(Args)>{})) test(int);
template<typename...>
static std::false_type test(...);
public:
using type = decltype(test<T, Args...>(0));
};
template<template<typename> class Map, typename T, typename... Args>
struct is_invokable : is_invokable_helper<Map, T, Args...>::type {};
template<template<typename...> class Trait, typename T>
struct autocall_trait_helper {
private:
template<typename U, std::size_t I>
struct expand {
using type = Trait<U, meta_list_element_t<I, typename U::Autocall>>;
};
template<typename>
static std::false_type test_helper(...);
template<typename U, std::size_t... S, int_t<
enable_if_t<expand<U, S>::type::value>...> = 0>
static std::true_type test_helper(seq<S...>);
template<typename>
static std::true_type test(...);
template<typename U>
static decltype(test_helper<U>(detail::tuple_seq<typename U::Autocall>{})) test(int);
public:
using type = decltype(test<T>(0));
};
template<template<typename...> class Trait, typename T>
using autocall_trait = typename autocall_trait_helper<Trait, T>::type;
template<typename T, typename F>
struct is_autocall_entry_map_complete_helper {
#ifndef KGR_KANGARU_MSVC_NO_AUTOCALL_MAP_CHECK
private:
template<typename U, typename C, enable_if_t<is_invoke_call<C>::value, int> = 0>
static std::true_type test(...);
template<typename U, typename C, enable_if_t<!is_invoke_call<C>::value, int> = 0>
static std::false_type test(...);
template<typename>
static std::false_type test_helper(...);
template<template<typename> class Map, typename U, typename C, std::size_t... S, int_t<
service_map_t<Map, meta_list_element_t<S, function_arguments_t<typename C::value_type>>>...> = 0>
static std::true_type test_helper(seq<S...>);
template<typename U, typename C>
static decltype(test_helper<U::template Map, U, C>(tuple_seq<function_arguments_t<typename C::value_type>>{})) test(int);
public:
using type = decltype(test<T, F>(0));
#else
using type = std::true_type;
#endif
};
template<typename T, typename F>
using is_autocall_entry_map_complete = typename is_autocall_entry_map_complete_helper<T, F>::type;
template<typename T, typename F>
struct is_autocall_entry_valid_helper {
private:
// This is workaround for clang. Clang will not interpret the name of the class itself
// as a valid template template parameter. using this alias, it forces it to use the name as a template.
template<typename U, typename C>
using self_t = typename is_autocall_entry_valid_helper<U, C>::type;
struct invoke_method_condition {
template<typename U, typename C, std::size_t I>
using type = std::integral_constant<bool,
service_check<meta_list_element_t<I, autocall_arguments_t<U, C>>>::value &&
dependency_check<meta_list_element_t<I, autocall_arguments_t<U, C>>>::value>;
};
struct invoke_call_condition {
template<typename U, typename C, std::size_t I>
using type = is_invoke_call<C>;
};
template<typename U, typename C, std::size_t I>
struct expander {
using type = typename std::conditional<is_autocall_entry_map_complete<U, C>::value, invoke_method_condition, invoke_call_condition>::type::template type<U, C, I>::type;
};
template<typename...>
static std::false_type test(...);
template<typename>
static std::false_type test_helper(...);
template<typename U, typename C, std::size_t... S, int_t<
enable_if_t<expander<U, C, S>::type::value>...> = 0>
static std::true_type test_helper(seq<S...>);
template<typename U, typename C, enable_if_t<is_valid_autocall_function<U, C>::value, int> = 0>
static decltype(test_helper<U, C>(tuple_seq<function_arguments_t<typename C::value_type>>{})) test(int);
public:
using type = decltype(test<T, F>(0));
};
template<typename T, typename F>
using is_autocall_entry_valid = typename is_autocall_entry_valid_helper<T, F>::type;
template<typename T>
using is_autocall_valid = autocall_trait<is_autocall_entry_valid, T>;
template<typename T, typename... Args>
struct is_service_valid : std::integral_constant<bool,
is_single_no_args<T, Args...>::value &&
service_check<T, Args...>::value &&
dependency_check<T, Args...>::value &&
is_autocall_valid<T>::value &&
dependency_trait<is_autocall_valid, T, Args...>::value
> {};
template<template<typename> class Map, typename T, typename... Args>
struct is_invoke_service_valid_helper {
private:
template<typename U, std::size_t I>
struct expander {
using type = std::integral_constant<bool, is_service_valid<service_map_t<Map, invoke_function_argument_t<I, Map, U, Args...>>>::value>;
};
template<typename U>
using map_t = service_map_t<Map, U>;
template<typename U, typename... As, std::size_t... S, int_t<map_t<invoke_function_argument_t<S, Map, U, As...>>..., enable_if_t<expander<U, S>::type::value>...> = 0>
static std::true_type test_helper(seq<S...>);
template<typename...>
static std::false_type test_helper(...);
template<typename U, typename... As>
static decltype(test_helper<U, As...>(tuple_seq_minus<invoke_function_arguments_t<Map, U, As...>, sizeof...(Args)>{})) test(int);
template<typename...>
static std::false_type test(...);
public:
using type = decltype(test<T, Args...>(0));
};
template<template<typename> class Map, typename T, typename... Args>
struct is_invoke_service_valid : is_invoke_service_valid_helper<Map, T, Args...>::type {};
template<template<typename> class Map, typename T, typename... Args>
using is_invoke_valid = std::integral_constant<bool,
is_invoke_service_valid<Map, T, Args...>::value &&
is_invokable<Map, T, Args...>::value
>;
} // namespace detail
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_TRAITS_HPP

View File

@@ -0,0 +1,74 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SINGLE_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SINGLE_HPP
#include "traits.hpp"
#include "meta_list.hpp"
namespace kgr {
struct Single {
Single() = default;
~Single() = default;
Single(const Single&) = delete;
Single& operator=(const Single&) = delete;
Single(Single&&) = default;
Single& operator=(Single&&) = default;
};
struct Abstract {};
template<typename T>
struct Default {
using DefaultService = T;
};
template<typename... Types>
struct Overrides {
using ParentTypes = detail::meta_list<Types...>;
};
namespace detail {
template<typename, typename = void>
struct parent_type_helper {
using ParentTypes = meta_list<>;
};
template<typename T>
struct parent_type_helper<T, void_t<typename T::ParentTypes>> {
using ParentTypes = typename T::ParentTypes;
};
template<typename T>
using parent_types = typename parent_type_helper<T>::ParentTypes;
template<typename, typename = void>
struct default_type_helper {
using has_default = std::false_type;
};
template<typename T>
struct default_type_helper<T, void_t<typename T::DefaultService>> {
using has_default = std::true_type;
using Service = typename T::DefaultService;
};
template<typename T>
using default_type = typename default_type_helper<T>::Service;
template<typename T>
using has_default = typename default_type_helper<T>::has_default;
template<typename T>
using is_abstract_service = std::integral_constant<bool, std::is_base_of<Abstract, T>::value || std::is_abstract<T>::value>;
template<typename T>
using is_single = std::integral_constant<bool, std::is_base_of<Single, T>::value || is_abstract_service<T>::value>;
template<typename Service, typename Overrider>
using is_overriden_by = meta_list_contains<Service, parent_types<Overrider>>;
} // namespace detail
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SINGLE_HPP

View File

@@ -0,0 +1,205 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_TRAITS_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_TRAITS_HPP
#include "function_traits.hpp"
#include "utils.hpp"
#include "meta_list.hpp"
#include "void_t.hpp"
#include <type_traits>
#include <tuple>
namespace kgr {
namespace detail {
template<typename...>
struct to_false {
using type = std::false_type;
};
template<typename... Ts>
using false_t = typename to_false<Ts...>::type;
template<typename...>
struct to_int {
using type = int;
};
// Workaround for visual studio to take the address of a generic lambda
template<typename T>
T exact(T);
template<typename... Ts>
using int_t = typename to_int<Ts...>::type;
template<typename T>
struct identity { using type = T; };
template<typename T>
using identity_t = typename identity<T>::type;
// things missing from c++11 (to be removed when switching to c++14)
template <bool b, typename T = void>
using enable_if_t = typename std::enable_if<b, T>::type;
template<typename T>
using decay_t = typename std::decay<T>::type;
template<std::size_t S, typename T>
using tuple_element_t = typename std::tuple_element<S, T>::type;
template<std::size_t size, std::size_t align>
using aligned_storage_t = typename std::aligned_storage<size, align>::type;
template<std::size_t ...>
struct seq {};
template<std::size_t n, std::size_t ...S>
struct seq_gen : seq_gen<n-1, n-1, S...> {};
template<std::size_t ...S>
struct seq_gen<0, S...> {
using type = seq<S...>;
};
template<typename>
struct TupleSeqGen;
template<typename... Types>
struct TupleSeqGen<std::tuple<Types...>> : seq_gen<sizeof...(Types)> {};
template<typename... Types>
struct TupleSeqGen<detail::meta_list<Types...>> : seq_gen<sizeof...(Types)> {};
template<typename Tuple>
using tuple_seq = typename TupleSeqGen<Tuple>::type;
template<typename F>
using function_seq = tuple_seq<function_arguments_t<F>>;
template<typename List, int n>
using tuple_seq_minus = typename detail::seq_gen<meta_list_size<List>::value - (n > meta_list_size<List>::value ? meta_list_size<List>::value : n)>::type;
// SFINAE utilities
template<typename From, typename To>
using is_explicitly_convertible = std::is_constructible<To, From>;
template<typename T, typename = void>
struct has_autocall : std::false_type {};
template<typename T>
struct has_autocall<T, void_t<typename T::Autocall>> : std::true_type {};
template<typename T, typename = void>
struct has_forward : std::false_type {};
template<typename T>
struct has_forward<T, void_t<decltype(std::declval<T>().forward())>> : std::true_type {};
template<typename T, typename = void>
struct is_service : std::false_type {};
template<typename T>
struct is_service<T, enable_if_t<(!std::is_polymorphic<T>::value || std::is_abstract<T>::value) && has_forward<T>::value>> : std::true_type {};
// Here, usual traits using void_t don't quite work with visual studio for this particular case.
template<typename T>
struct has_construct_helper {
private:
template<typename U, typename V = decltype(&U::construct)>
static std::true_type test(int);
template<typename>
static std::false_type test(...);
public:
using type = decltype(test<T>(0));
};
template<typename T>
using has_construct = typename has_construct_helper<T>::type;
template<typename T, typename = void>
struct is_invoke_call : std::false_type {};
template<typename T>
struct is_invoke_call<T, void_t<typename T::Parameters>> : std::true_type {};
template<typename T, typename F>
using is_member_autocall = std::integral_constant<bool, !is_invoke_call<F>::value>;
template<template<typename> class Map, typename T, typename = void>
struct is_complete_map : std::false_type {};
template<template<typename> class Map, typename T>
struct is_complete_map<Map, T, void_t<typename Map<T>::Service>> : std::true_type {};
struct Sink {
constexpr Sink() = default;
template<typename T>
constexpr operator T&& () const;
template<typename T>
constexpr operator const T& () const;
};
template<typename T, typename... Args>
struct is_brace_constructible_helper {
private:
template<typename U, typename... As>
static decltype(static_cast<void>(U{std::declval<As>()...}), std::true_type{}) test(int);
template<typename...>
static std::false_type test(...);
public:
using type = decltype(test<T, Args...>(0));
};
template<typename T, typename... Args>
struct has_emplace_helper {
private:
template<typename U, typename... As>
static decltype(static_cast<void>(std::declval<U>().emplace(std::declval<As>()...)), std::true_type{}) test(int);
template<typename U, typename... As>
static std::false_type test(...);
public:
using type = decltype(test<T, Args...>(0));
};
template<typename T>
using is_service_embeddable = std::integral_constant<bool,
std::is_trivially_destructible<T>::value &&
sizeof(T) <= sizeof(void*) && alignof(T) <= alignof(void*)
>;
template<typename T, typename... Args>
using has_emplace = typename has_emplace_helper<T, Args...>::type;
template<typename T, typename... Args>
using is_brace_constructible = typename is_brace_constructible_helper<T, Args...>::type;
template<typename T, typename... Args>
using is_only_brace_constructible = std::integral_constant<bool, is_brace_constructible<T, Args...>::value && !std::is_constructible<T, Args...>::value>;
template<typename T> struct remove_rvalue_reference { using type = T; };
template<typename T> struct remove_rvalue_reference<T&&> { using type = T; };
template<typename T> using remove_rvalue_reference_t = typename remove_rvalue_reference<T>::type;
template<typename T, typename... Args>
using is_someway_constructible = std::integral_constant<bool, is_brace_constructible<T, Args...>::value || std::is_constructible<T, Args...>::value>;
template<typename T, typename... Args>
using is_emplaceable = std::integral_constant<bool, std::is_default_constructible<T>::value && has_emplace<T, Args...>::value>;
template<typename T, typename... Args>
using is_service_instantiable = std::integral_constant<bool, is_emplaceable<T, Args...>::value || is_someway_constructible<T, kgr::in_place_t, Args...>::value>;
} // namespace detail
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_TRAITS_HPP

View File

@@ -0,0 +1,54 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_UTILS_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_UTILS_HPP
#include <type_traits>
namespace kgr {
namespace detail {
struct no_autocall_t {};
template<typename, typename = void>
struct ServiceTypeHelper {};
template<typename T>
struct ServiceTypeHelper<T, decltype(void(std::declval<T>().forward()))> {
using type = decltype(std::declval<T>().forward());
};
} // namespace detail
template<typename...>
struct Map;
template<>
struct Map<> {};
template<typename T>
struct Map<T> {
using Service = T;
};
template<typename T>
using ServiceType = typename detail::ServiceTypeHelper<T>::type;
struct in_place_t{};
constexpr in_place_t in_place{};
constexpr detail::no_autocall_t no_autocall{};
namespace detail {
template<template<typename> class Map, typename T>
using service_map_t = typename Map<T>::Service;
template<typename T>
struct is_map : std::false_type {};
template<typename... Ts>
struct is_map<kgr::Map<Ts...>> : std::true_type {};
} // namespace detail
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_UTILS_HPP

View File

@@ -0,0 +1,16 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_VOID_T_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_VOID_T_HPP
namespace kgr {
namespace detail {
// void_t implementation
template<typename...>
struct voider { using type = void; };
template<typename... Ts> using void_t = typename voider<Ts...>::type;
} // namespace detail
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_VOID_T_HPP

View File

@@ -0,0 +1,101 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_GENERIC_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_GENERIC_HPP
#include "container.hpp"
#include "detail/injected.hpp"
#include "detail/traits.hpp"
namespace kgr {
namespace detail {
template<typename Generic, typename Type, typename = void>
struct GenericServiceDestruction {
~GenericServiceDestruction() {
static_cast<Generic&>(*this).instance().~Type();
}
};
template<typename Generic, typename Type>
struct GenericServiceDestruction<Generic, Type, enable_if_t<std::is_trivially_destructible<Type>::value>> {};
template<typename, typename> struct autocall_function;
} // namespace detail
template<typename CRTP, typename Type>
struct GenericService : detail::GenericServiceDestruction<GenericService<CRTP, Type>, Type> {
friend struct Container;
using Self = CRTP;
GenericService() = default;
GenericService(GenericService&& other) {
emplace(std::move(other.instance()));
}
GenericService& operator=(GenericService&& other) {
emplace(std::move(other.instance()));
return *this;
}
GenericService(const GenericService& other) {
emplace(other.instance());
}
GenericService& operator=(const GenericService& other) {
emplace(other.instance());
return *this;
}
template<typename... Args, detail::enable_if_t<detail::is_someway_constructible<Type, Args...>::value, int> = 0>
GenericService(in_place_t, Args&&... args) {
emplace(std::forward<Args>(args)...);
}
protected:
Type& instance() {
return *reinterpret_cast<Type*>(&_instance);
}
const Type& instance() const {
return *reinterpret_cast<const Type*>(&_instance);
}
private:
friend struct detail::GenericServiceDestruction<GenericService<CRTP, Type>, Type>;
template<typename, typename...> friend struct detail::has_emplace_helper;
template<typename, typename> friend struct detail::autocall_function;
template<typename... Args, detail::enable_if_t<std::is_constructible<Type, Args...>::value, int> = 0>
void emplace(Args&&... args) {
new (&_instance) Type(std::forward<Args>(args)...);
}
template<typename... Args, detail::enable_if_t<detail::is_only_brace_constructible<Type, Args...>::value, int> = 0>
void emplace(Args&&... args) {
new (&_instance) Type{std::forward<Args>(args)...};
}
template<typename F, typename... Ts>
void autocall(Inject<Ts>... others) {
CRTP::call(instance(), F::value, std::forward<Inject<Ts>>(others).forward()...);
}
template<typename F, template<typename> class Map>
void autocall(Inject<ContainerService> cs) {
autocall<Map, F>(detail::tuple_seq<detail::function_arguments_t<typename F::value_type>>{}, std::move(cs));
}
template<template<typename> class Map, typename F, std::size_t... S>
void autocall(detail::seq<S...>, Inject<ContainerService> cs) {
cs.forward().invoke<Map>([this](detail::function_argument_t<S, typename F::value_type>... args){
CRTP::call(instance(), F::value, std::forward<detail::function_argument_t<S, typename F::value_type>>(args)...);
});
}
detail::aligned_storage_t<sizeof(Type), alignof(Type)> _instance;
};
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_GENERIC_HPP

View File

@@ -0,0 +1,13 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_KANGARU_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_KANGARU_HPP
#include "container.hpp"
#include "generic.hpp"
#include "operator.hpp"
#include "operator_service.hpp"
#include "predicate.hpp"
#include "debug.hpp"
#include "service.hpp"
#include "type_id.hpp"
#endif // KGR_KANGARU_INCLUDE_KANGARU_KANGARU_HPP

View File

@@ -0,0 +1,146 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_HPP
#include "container.hpp"
#include "detail/lazy_base.hpp"
namespace kgr {
namespace detail {
template<typename CRTP, template<typename> class Map>
struct InvokerBase {
template<typename F, typename... Args, enable_if_t<is_invoke_valid<Map, decay_t<F>, Args...>::value, int> = 0>
invoke_function_result_t<Map, decay_t<F>, Args...> operator()(F&& f, Args&&... args) {
return static_cast<CRTP*>(this)->container().template invoke<Map>(std::forward<F>(f), std::forward<Args>(args)...);
}
Sink operator()(detail::NotInvokableError = {}, ...) = delete;
};
template<typename CRTP, typename T>
struct GeneratorBase {
static_assert(!is_single<T>::value, "Generator only work with non-single services.");
template<typename... Args, enable_if_t<is_service_valid<T, Args...>::value, int> = 0>
ServiceType<T> operator()(Args&&... args) {
return static_cast<CRTP*>(this)->container().template service<T>(std::forward<Args>(args)...);
}
template<typename U = T, enable_if_t<std::is_default_constructible<ServiceError<U>>::value, int> = 0>
Sink operator()(ServiceError<U> = {}) = delete;
template<typename... Args>
Sink operator()(ServiceError<T, identity_t<Args>...>, Args&&...) = delete;
};
} // namespace detail
template<template<typename> class Map>
struct Invoker : detail::InvokerBase<Invoker<Map>, Map> {
explicit Invoker(Container& container) : _container{&container} {}
private:
kgr::Container& container() {
return *_container;
}
const kgr::Container& container() const {
return *_container;
}
friend struct detail::InvokerBase<Invoker<Map>, Map>;
kgr::Container* _container;
};
using DefaultInvoker = Invoker<kgr::AdlMap>;
template<template<typename> class Map>
struct ForkedInvoker : detail::InvokerBase<ForkedInvoker<Map>, Map> {
explicit ForkedInvoker(Container container) : _container{std::move(container)} {}
private:
kgr::Container& container() {
return _container;
}
const kgr::Container& container() const {
return _container;
}
friend struct detail::InvokerBase<ForkedInvoker<Map>, Map>;
kgr::Container _container;
};
using DefaultForkedInvoker = ForkedInvoker<kgr::AdlMap>;
template<typename T>
struct Generator : detail::GeneratorBase<Generator<T>, T> {
explicit Generator(Container& container) : _container{&container} {}
private:
kgr::Container& container() {
return *_container;
}
const kgr::Container& container() const {
return *_container;
}
friend struct detail::GeneratorBase<Generator<T>, T>;
kgr::Container* _container;
};
template<typename T>
struct ForkedGenerator : detail::GeneratorBase<ForkedGenerator<T>, T> {
explicit ForkedGenerator(Container container) : _container{std::move(container)} {}
private:
kgr::Container& container() {
return _container;
}
const kgr::Container& container() const {
return _container;
}
friend struct detail::GeneratorBase<ForkedGenerator<T>, T>;
kgr::Container _container;
};
template<typename T>
struct Lazy : detail::LazyBase<Lazy<T>, T> {
explicit Lazy(kgr::Container& container) : _container{&container} {}
private:
kgr::Container& container() {
return *_container;
}
const kgr::Container& container() const {
return *_container;
}
friend struct detail::LazyBase<Lazy<T>, T>;
kgr::Container* _container;
};
template<typename T>
struct ForkedLazy : detail::LazyBase<ForkedLazy<T>, T> {
explicit ForkedLazy(kgr::Container container) : _container{std::move(container)} {}
private:
kgr::Container& container() {
return _container;
}
const kgr::Container& container() const {
return _container;
}
friend struct detail::LazyBase<ForkedLazy<T>, T>;
kgr::Container _container;
};
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_HPP

View File

@@ -0,0 +1,149 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_SERVICE_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_SERVICE_HPP
#include "container.hpp"
#include "operator.hpp"
namespace kgr {
template<typename Predicate>
struct FilteredForkService {
explicit FilteredForkService(in_place_t, Container& container) : _container{container.fork<Predicate>()} {}
inline Container forward() {
return std::move(_container);
}
static auto construct(Inject<ContainerService> cs) -> decltype(inject(cs.forward())) {
return inject(cs.forward());
}
private:
Container _container;
};
using ForkService = FilteredForkService<All>;
auto service_map(Container&&) -> ForkService;
template<template<typename> class Map>
struct InvokerService {
explicit InvokerService(in_place_t, Container& container) : _container{container} {}
Invoker<Map> forward() {
return Invoker<Map>{_container};
}
static auto construct(Inject<ContainerService> cs) -> decltype(inject(cs.forward())) {
return inject(cs.forward());
}
private:
Container& _container;
};
using DefaultInvokerService = InvokerService<kgr::AdlMap>;
template<template<typename> class Map>
auto service_map(const Invoker<Map>&) -> InvokerService<Map>;
template<template<typename> class Map, typename Predicate = All>
struct ForkedInvokerService {
explicit ForkedInvokerService(in_place_t, Container& container) : _container{container.fork<Predicate>()} {}
ForkedInvoker<Map> forward() {
return ForkedInvoker<Map>{std::move(_container)};
}
static auto construct(Inject<ContainerService> cs) -> decltype(inject(cs.forward())) {
return inject(cs.forward());
}
private:
Container _container;
};
using DefaultForkedInvokerService = ForkedInvokerService<kgr::AdlMap>;
template<template<typename> class Map>
auto service_map(const ForkedInvoker<Map>&) -> ForkedInvokerService<Map>;
template<typename T>
struct GeneratorService {
explicit GeneratorService(in_place_t, Container& container) : _container{container} {}
Generator<T> forward() {
return Generator<T>{_container};
}
static auto construct(Inject<ContainerService> cs) -> decltype(inject(cs.forward())) {
return inject(cs.forward());
}
private:
Container& _container;
};
template<typename T>
auto service_map(const Generator<T>&) -> GeneratorService<T>;
template<typename T, typename Predicate = All>
struct ForkedGeneratorService {
explicit ForkedGeneratorService(in_place_t, Container& container) : _container{container.fork<Predicate>()} {}
ForkedGenerator<T> forward() {
return ForkedGenerator<T>{std::move(_container)};
}
static auto construct(Inject<ContainerService> cs) -> decltype(inject(cs.forward())) {
return inject(cs.forward());
}
private:
Container _container;
};
template<typename T>
auto service_map(const ForkedGenerator<T>&) -> ForkedGeneratorService<T>;
template<typename T>
struct LazyService {
explicit LazyService(in_place_t, Container& container) : _container{container} {}
Lazy<T> forward() {
return Lazy<T>{_container};
}
static auto construct(Inject<ContainerService> cs) -> decltype(inject(cs.forward())) {
return inject(cs.forward());
}
private:
Container& _container;
};
template<typename T>
auto service_map(const Lazy<T>&) -> LazyService<T>;
template<typename T, typename Predicate = All>
struct ForkedLazyService {
explicit ForkedLazyService(in_place_t, Container& container) : _container{container.fork<Predicate>()} {}
ForkedLazy<T> forward() {
return ForkedLazy<T>{std::move(_container)};
}
static auto construct(Inject<ContainerService> cs) -> decltype(inject(cs.forward())) {
return inject(cs.forward());
}
private:
Container _container;
};
template<typename T>
auto service_map(const ForkedLazy<T>&) -> ForkedLazyService<T>;
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_OPERATOR_SERVICE_HPP

View File

@@ -0,0 +1,52 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_PREDICATE_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_PREDICATE_HPP
#include "type_id.hpp"
namespace kgr {
struct All {
constexpr inline bool operator()(type_id_t) const {
return true;
}
};
template<typename First, typename... Ts>
struct NoneOf {
constexpr bool operator()(type_id_t id) const {
return !compare<type_id<First>, type_id<Ts>...>(id);
}
private:
template<type_id_t comp, type_id_t second, type_id_t... others>
constexpr bool compare(type_id_t id) const {
return id == comp && compare<second, others...>(id);
}
template<type_id_t comp>
constexpr bool compare(type_id_t id) const {
return id == comp;
}
};
template<typename First, typename... Ts>
struct AnyOf {
constexpr bool operator()(type_id_t id) const {
return compare<type_id<First>, type_id<Ts>...>(id);
}
private:
template<type_id_t comp, type_id_t second, type_id_t... others>
constexpr bool compare(type_id_t id) const {
return id == comp && compare<second, others...>(id);
}
template<type_id_t comp>
constexpr bool compare(type_id_t id) const {
return id == comp;
}
};
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_PREDICATE_HPP

View File

@@ -0,0 +1,130 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_SERVICE_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_SERVICE_HPP
#include <type_traits>
#include "detail/utils.hpp"
#include "detail/single.hpp"
#include "generic.hpp"
namespace kgr {
template<typename... Args>
struct Dependency {};
template<typename, typename = Dependency<>>
struct SingleService;
template<typename Type, typename... Deps>
struct SingleService<Type, Dependency<Deps...>> : GenericService<SingleService<Type, Dependency<Deps...>>, Type>, Single {
private: using Parent = GenericService<SingleService<Type, Dependency<Deps...>>, Type>;
public:
using typename Parent::Self;
using Parent::Parent;
static auto construct(Inject<Deps>... deps) -> decltype(inject(deps.forward()...)) {
return inject(deps.forward()...);
}
Type& forward() {
return this->instance();
}
template<typename T, typename... Args>
static detail::function_result_t<T> call(Type& instance, T method, Args&&... args) {
return (instance.*method)(std::forward<Args>(args)...);
}
};
template<typename, typename = Dependency<>>
struct Service;
template<typename Type, typename... Deps>
struct Service<Type, Dependency<Deps...>> : GenericService<Service<Type, Dependency<Deps...>>, Type> {
private: using Parent = GenericService<Service<Type, Dependency<Deps...>>, Type>;
public:
using typename Parent::Self;
using Parent::Parent;
template<typename... Args>
static auto construct(Inject<Deps>... deps, Args&&... args) -> decltype(inject(deps.forward()..., std::declval<Args>()...)) {
return inject(deps.forward()..., std::forward<Args>(args)...);
}
Type forward() {
return std::move(this->instance());
}
template<typename T, typename... Args>
static detail::function_result_t<T> call(Type& instance, T method, Args&&... args) {
return (instance.*method)(std::forward<Args>(args)...);
}
};
template<typename, typename = Dependency<>>
struct UniqueService;
template<typename Type, typename... Deps>
struct UniqueService<Type, Dependency<Deps...>> : GenericService<UniqueService<Type, Dependency<Deps...>>, std::unique_ptr<Type>> {
private: using Parent = GenericService<UniqueService<Type, Dependency<Deps...>>, std::unique_ptr<Type>>;
public:
using typename Parent::Self;
using Parent::Parent;
template<typename... Args>
static auto construct(Inject<Deps>... deps, Args&&... args)
-> decltype(inject(std::unique_ptr<Type>{new Type{deps.forward()..., std::declval<Args>()...}})) {
return inject(std::unique_ptr<Type>{new Type{deps.forward()..., std::forward<Args>(args)...}});
}
std::unique_ptr<Type> forward() {
return std::move(this->instance());
}
template<typename T, typename... Args>
static detail::function_result_t<T> call(std::unique_ptr<Type>& instance, T method, Args&&... args) {
return ((*instance).*method)(std::forward<Args>(args)...);
}
};
template<typename, typename = Dependency<>>
struct SharedService;
template<typename Type, typename... Deps>
struct SharedService<Type, Dependency<Deps...>> : GenericService<SharedService<Type, Dependency<Deps...>>, std::shared_ptr<Type>>, Single {
private: using Parent = GenericService<SharedService<Type, Dependency<Deps...>>, std::shared_ptr<Type>>;
public:
using typename Parent::Self;
using Parent::Parent;
static auto construct(Inject<Deps>... deps) -> decltype(inject(std::make_shared<Type>(deps.forward()...))) {
return inject(std::make_shared<Type>(deps.forward()...));
}
std::shared_ptr<Type> forward() {
return this->instance();
}
template<typename T, typename... Args>
static detail::function_result_t<T> call(std::shared_ptr<Type>& instance, T method, Args&&... args) {
return ((*instance).*method)(std::forward<Args>(args)...);
}
};
template<typename T>
struct AbstractService : Abstract {
T& forward();
};
template<typename T>
struct AbstractSharedService : Abstract {
std::shared_ptr<T> forward();
};
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_SERVICE_HPP

View File

@@ -0,0 +1,11 @@
#ifndef KGR_KANGARU_INCLUDE_KANGARU_TYPE_ID_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_TYPE_ID_HPP
namespace kgr {
using type_id_t = void(*)();
template <typename T> void type_id() {}
} // namespace kgr
#endif // KGR_KANGARU_INCLUDE_KANGARU_TYPE_ID_HPP