Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# CMake - https://github.com/github/gitignore/blob/master/CMake.gitignore
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Expand Down
23 changes: 23 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,29 @@ enable_testing()

add_library(mil INTERFACE)

target_compile_options(mil
INTERFACE

-Werror

-Wall
-Wextra
-Wpedantic

-Wcast-align
-Wcast-qual
-Wconversion
-Wctor-dtor-privacy
-Wenum-compare
-Wfloat-equal
-Wnon-virtual-dtor
-Wold-style-cast
-Woverloaded-virtual
-Wredundant-decls
-Wsign-conversion
-Wsign-promo
)

target_include_directories(mil
INTERFACE
$<INSTALL_INTERFACE:include>
Expand Down
183 changes: 174 additions & 9 deletions include/chain_invoke.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
/* STL */
#include <tuple>
#include <array>
#include <functional>
#include <type_traits>

/**
Expand Down Expand Up @@ -61,20 +62,70 @@ using namespace msl;
}
}

/**
* @brief TypeSelector will select what tuple of agrs will get from function
* tuple fow univsality and compatibility
* @tparam have_agrs have function args or no
* @tparam TRet return type of function
* @tparam stack_args tuple of args if function have_agrs
* @details Possible three variants which described below
// 1) if have agrs - then return type must be void and function params must
// be refs or pointers. Selected type is tuple (stack_args)
// 2) if have no agrs and return type is not ref or pointer then selected type is tuple<TRet>
// 3) if have no agrs and return type is ref or pointer then selected type is tuple<pointer<TRet>>
*/
template<bool have_agrs, typename TRet, typename stack_args>
struct TypeSelector final
{
private:
using stack_args_or_ret_value = std::conditional_t<have_agrs, stack_args, std::tuple<TRet>>;
inline static constexpr bool is_ref = std::is_reference_v<TRet>;
inline static constexpr bool is_pointer = std::is_pointer_v<TRet>;
using no_ref_but_pointer = std::tuple<std::add_pointer_t<std::remove_reference_t<TRet>>>;
using ret_pointer_or_ret_value = std::conditional_t<is_ref, no_ref_but_pointer, std::tuple<TRet>>;

public:
inline static constexpr bool have_agrs_value = have_agrs;
inline static constexpr bool is_ref_value = is_ref;
inline static constexpr bool is_pointer_value = is_pointer;
inline static constexpr bool is_copy_value = !have_agrs_value && !is_ref_value && !is_pointer_value;
using args_pointer_value = std::conditional_t<is_ref, ret_pointer_or_ret_value, stack_args_or_ret_value>;
using type = std::conditional_t<is_pointer, ret_pointer_or_ret_value, args_pointer_value>;

TypeSelector()
{
if constexpr(have_agrs_value)
{
static_assert(std::is_same_v<void, TRet>, "if have args then return value must be void");
}
if constexpr(! have_agrs_value)
{
static_assert(! std::is_same_v<void, TRet>, "if have no args then return value can't be void");
}
}
};

/**
* @brief The invoking step object, which also holds the tuple
*
* @tparam Fx Type of the method
*/
template<typename Fx>
struct OwningInvokingStep {
using tuple_t = typename function_info<Fx>::stack_args;
using qalified_t = typename function_info<Fx>::args;
using class_t = typename function_info<Fx>::cl;
struct OwningInvokingStep {
using function_info_t = function_info<Fx>;
using ret_t = typename function_info_t::ret;
inline static constexpr bool have_agrs = function_info_t::args_count > 0;
using type_selector_t = TypeSelector<have_agrs, ret_t, typename function_info_t::stack_args>;
using tuple_t = typename type_selector_t::type;
using qalified_t = typename function_info_t::args;
using class_t = typename function_info_t::cl;

static constexpr size_t TUPLE_SIZE { std::tuple_size_v<tuple_t> };
// not use dicrectly but in constructor have static assert
const function_info_t function_info_;

tuple_t tuple;
// only for call constructor type_selector_t and check static asserts
const type_selector_t ts;

/**
* @brief The streaming operator which simply deduces type of
Expand All @@ -88,8 +139,34 @@ using namespace msl;
*/
template<typename OpFx>
constexpr auto operator<<(OpFx const & aFx) && {
using invoking_t = typename function_info<OpFx>::cl;
return OwningInvokingStep<OpFx>{ aFx, std::get<invoking_t>(tuple) };
if constexpr (type_selector_t::have_agrs_value)
{
using invoking_t = typename function_info<OpFx>::cl;
return OwningInvokingStep<OpFx>{ aFx, std::get<invoking_t>(tuple) };
}
else
{
if constexpr(type_selector_t::is_copy_value)
{
static_assert(std::tuple_size_v<tuple_t> == 1, "tuple size must be 1");
return OwningInvokingStep<OpFx>{ aFx, std::get<0>(tuple) };
}
else if constexpr(type_selector_t::is_ref_value)
{
static_assert(std::tuple_size_v<tuple_t> == 1, "tuple size must be 1");
return OwningInvokingStep<OpFx>{ aFx, std::get<0>(tuple) };
}
else if constexpr(type_selector_t::is_pointer_value)
{
static_assert(std::tuple_size_v<tuple_t> == 1, "tuple size must be 1");
return OwningInvokingStep<OpFx>{ aFx, std::get<0>(tuple) };
}
else
{
// type_selector_t::have_agrs_value myst be false here
static_assert(type_selector_t::have_agrs_value, "unknown compile time branch");
}
}
}

/**
Expand All @@ -102,7 +179,39 @@ using namespace msl;
explicit constexpr OwningInvokingStep(Fx const & aFx, Obj & obj)
: tuple { }
{
this->invokeImpl(std::make_index_sequence<TUPLE_SIZE>{}, aFx, obj);
if constexpr (type_selector_t::have_agrs_value)
{
using compare_obj_t = std::remove_const_t<std::remove_pointer_t<Obj>>;
static_assert(std::is_same_v<std::remove_const_t<class_t>, compare_obj_t>, "must be one type");

this->invokeImpl(std::make_index_sequence<function_info_t::args_count>{}, aFx, obj);
}
else if constexpr (type_selector_t::is_copy_value)
{
using compare_obj_t = std::remove_const_t<std::remove_pointer_t<Obj>>;
static_assert(std::is_same_v<std::remove_const_t<class_t>, compare_obj_t>, "must be one type");

this->invokeImplWithret(aFx, obj);
}
else if constexpr (type_selector_t::is_ref_value)
{
using compare_obj_t = std::remove_const_t<std::remove_pointer_t<Obj>>;
static_assert(std::is_same_v<std::remove_const_t<class_t>, compare_obj_t>, "must be one type");

this->invokeImplWithRefret(aFx, obj);
}
else if constexpr (type_selector_t::is_pointer_value)
{
using compare_obj_t = std::remove_const_t<std::remove_pointer_t<Obj>>;
static_assert(std::is_same_v<std::remove_const_t<class_t>, compare_obj_t>, "must be one type");

this->invokeImplWithPtrret(aFx, obj);
}
else
{
// type_selector_t::have_agrs_value myst be false here
static_assert(type_selector_t::have_agrs_value, "unknown compile time branch");
}
}
private:
/**
Expand All @@ -113,8 +222,65 @@ using namespace msl;
*/
template<typename Obj, size_t ... Idx>
constexpr void invokeImpl(std::index_sequence<Idx...>, Fx const & aFx, Obj & obj) {
using compare_obj_t = std::remove_const_t<Obj>;
static_assert(std::is_same_v<std::remove_const_t<class_t>, compare_obj_t>, "must be one type");

(obj.*aFx)(conditionalAddressOf<std::tuple_element_t<Idx, qalified_t>>(std::get<Idx>(tuple))...);
}
template<typename Obj, size_t ... Idx>
constexpr void invokeImpl(std::index_sequence<Idx...>, Fx const & aFx, Obj* obj) {
using compare_obj_t = std::remove_const_t<std::remove_pointer_t<Obj>>;
static_assert(std::is_same_v<std::remove_const_t<class_t>, compare_obj_t>, "must be one type");
if(obj == nullptr) {
throw std::runtime_error(std::string{"object nullptr at "} + __FUNCTION__);
}

(obj->*aFx)(conditionalAddressOf<std::tuple_element_t<Idx, qalified_t>>(std::get<Idx>(tuple))...);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See not any check that obj is not nullptr.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I will fix. But better exception as i think. If all ok exception will be cheap.

}

template<typename Obj>
constexpr void invokeImplWithret(Fx const & aFx, Obj & obj) {
using compare_obj_t = std::remove_const_t<std::remove_pointer_t<Obj>>;
static_assert(std::is_same_v<std::remove_const_t<class_t>, compare_obj_t>, "must be one type");
if constexpr(std::is_pointer_v<Obj>){
if(obj == nullptr) {
throw std::runtime_error(std::string{"object nullptr at "} + __FUNCTION__);
}
}

std::get<0>(tuple) = std::invoke(aFx, obj);
}

template<typename Obj>
constexpr void invokeImplWithRefret(Fx const & aFx, Obj & obj) {
using compare_obj_t = std::remove_const_t<std::remove_pointer_t<Obj>>;
static_assert(std::is_same_v<std::remove_const_t<class_t>, compare_obj_t>, "must be one type");
if constexpr(std::is_pointer_v<Obj>){
if(obj == nullptr) {
throw std::runtime_error(std::string{"object nullptr at "} + __FUNCTION__);
}
}

decltype(auto) ret_val = std::invoke(aFx, obj);
static_assert(std::is_reference_v<decltype(ret_val)>, "ret value must be reference");
std::get<0>(tuple) = &ret_val;
}

template<typename Obj>
constexpr void invokeImplWithPtrret(Fx const & aFx, Obj & obj) {
// sometimes object can be ref to pointer
using compare_obj_t = std::remove_const_t<std::remove_pointer_t<Obj>>;
static_assert(std::is_same_v<std::remove_const_t<class_t>, compare_obj_t>, "must be one type");
if constexpr(std::is_pointer_v<Obj>){
if(obj == nullptr) {
throw std::runtime_error(std::string{"object nullptr at "} + __FUNCTION__);
}
}

decltype(auto) ret_val = std::invoke(aFx, obj);
static_assert(std::is_pointer_v<decltype(ret_val)>, "ret value must be pointer");
std::get<0>(tuple) = ret_val;
}
};

/**
Expand Down Expand Up @@ -146,7 +312,6 @@ using namespace msl;
*/
template<typename OpFx>
constexpr auto operator<<(OpFx const & aFx) && {
using invoking_t = typename function_info<OpFx>::cl;
return OwningInvokingStep<OpFx>{ aFx, obj };
}
};
Expand Down
Loading