2024-11-14 22:10:43 +01:00
|
|
|
/* try.cpp - to try out and experiment with new features....
|
2007-08-17 00:36:07 +02:00
|
|
|
* scons will create the binary bin/try
|
2014-08-16 02:04:29 +02:00
|
|
|
*/
|
2025-06-22 19:42:03 +02:00
|
|
|
// 06/25 - provide a concept to accept _tuple-like_ objects
|
clean-up: simplify function-closure -- avoiding `std::function`
A very performance relevant shortcoming of the existing implementation
of partial function closure is that the result is always wrapped into a
std::function, which typically causes a heap allocation when more than
a single pre-bound argument must be stored — which is annoying,
since the underlying Binder provides inline storage and thus
could be handled directly as a value object.
However, returning the Binder directly is also problematic, since
this object is outfitted with several overloaded function call operators,
which defeats most techniques to detect a function signature. Notably,
relevant down-stream metaprogramming code, like the tuple-closure used
in the `NodeBuilder` would break when being confronted directly with
a binder object.
An investigation shows that there is no direct remedy, short of
wrapping the binder into another functor. This can be accomplished
with a helper template, that generates a wrapper; however, this
wrapper builder must be supplied with explicit type information
regarding the function arguments (precisely because this type
signature can not be picked up from the Binder object itself)
2025-06-06 19:44:24 +02:00
|
|
|
// 06/25 - investigate function type detection of std::bind Binders
|
Buffer-Provider: investigate Problem with embedded type-constructor-arguments
This is a possible extension which frequently comes up again during the design of the Engine.
Basically, the `TypeHandler` in the metadata-descriptor used by the `BufferProvder` could capture
additional context-arguments, which are then later passed to an object instance embedded into the buffer.
Yesterday I attempted to use this feature for a simple demonstration in `NodeBasic_test`,
just to find out that passing additional constructor arguments to the capture fails with
a confusing compilation error message. This failure could be traced down to the function binder;
and what at first sight seemed to be a compiler error, turned out to be a quite logical limitation:
When we »close« some objects of the constructor, but delay the construction itself, we'll have to
store a copy in the constructor-λ. And this implies, that we'll have to change the types
used for instantiation of the compiler, so that the construction-function can be invoked
by passing references from the captured copy of the additional arguments.
When naively passing those forwarded arguments into the std::bind()-call,
the resulting functor will fail at instantiation, when the compiler attempts
to generate the function-call `operator()`
see: https://stackoverflow.com/q/30968573/444796
2024-12-16 23:01:57 +01:00
|
|
|
// 12/24 - investigate problem when perfect-forwarding into a binder
|
2024-12-12 04:38:55 +01:00
|
|
|
// 12/24 - investigate overload resolution on a templated function similar to std::get
|
2024-11-14 22:10:43 +01:00
|
|
|
// 11/24 - how to define a bare object location comparison predicate
|
2023-11-27 21:58:37 +01:00
|
|
|
// 11/23 - prototype for grouping from iterator
|
2012-01-07 03:28:12 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @file try.cpp
|
2025-06-22 19:42:03 +02:00
|
|
|
* Develop a concept to detect _tuple-like_ classes, based on the requirements
|
|
|
|
|
* of the »tuple protocol«. Using some ideas from [Stackoverflow] as starting point.
|
2025-06-23 23:02:15 +02:00
|
|
|
* However, we model both a _friend function_ `get` and a similar member function
|
|
|
|
|
* as alternatives for element access, and we break down the checks into sub-concepts.
|
2025-06-22 19:42:03 +02:00
|
|
|
* [Stackoverflow]: https://stackoverflow.com/q/68443804/444796
|
2012-01-07 03:28:12 +01:00
|
|
|
*/
|
|
|
|
|
|
2020-03-07 19:39:51 +01:00
|
|
|
|
2016-01-06 04:36:53 +01:00
|
|
|
#include "lib/format-cout.hpp"
|
2018-03-17 03:36:58 +01:00
|
|
|
#include "lib/test/test-helper.hpp"
|
2022-12-18 03:47:40 +01:00
|
|
|
#include "lib/test/diagnostic-output.hpp"
|
2025-06-22 19:42:03 +02:00
|
|
|
#include "lib/meta/tuple-helper.hpp"
|
|
|
|
|
#include "lib/hetero-data.hpp"
|
2018-08-16 21:40:10 +02:00
|
|
|
#include "lib/util.hpp"
|
2019-04-19 18:37:30 +02:00
|
|
|
|
2025-06-22 19:42:03 +02:00
|
|
|
#include <concepts>
|
|
|
|
|
|
|
|
|
|
using std::string;
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
namespace meta {
|
|
|
|
|
|
|
|
|
|
template<class TUP>
|
|
|
|
|
concept tuple_sized = requires
|
|
|
|
|
{
|
|
|
|
|
{ std::tuple_size<TUP>::value } -> std::convertible_to<size_t>;
|
|
|
|
|
};
|
|
|
|
|
|
2025-06-23 23:02:15 +02:00
|
|
|
|
|
|
|
|
template<class TUP, std::size_t idx>
|
2025-06-22 19:42:03 +02:00
|
|
|
concept tuple_adl_accessible = requires(TUP tup)
|
|
|
|
|
{
|
2025-06-23 23:02:15 +02:00
|
|
|
typename std::tuple_element_t<idx, TUP>;
|
|
|
|
|
{ get<idx>(tup) } -> std::convertible_to<std::tuple_element_t<idx, TUP>&>;
|
2025-06-22 19:42:03 +02:00
|
|
|
};
|
2025-06-23 23:02:15 +02:00
|
|
|
|
|
|
|
|
template<class TUP, std::size_t idx>
|
2025-06-22 19:42:03 +02:00
|
|
|
concept tuple_mem_accessible = requires(TUP tup)
|
|
|
|
|
{
|
2025-06-23 23:02:15 +02:00
|
|
|
typename std::tuple_element_t<idx, TUP>;
|
|
|
|
|
{ tup.template get<idx>() } -> std::convertible_to<std::tuple_element_t<idx, TUP>&>;
|
2025-06-22 19:42:03 +02:00
|
|
|
};
|
2025-06-23 23:02:15 +02:00
|
|
|
|
|
|
|
|
template<class TUP, std::size_t idx>
|
|
|
|
|
concept tuple_element_accessible = tuple_mem_accessible<TUP,idx> or tuple_adl_accessible<TUP,idx>;
|
2025-06-22 19:42:03 +02:00
|
|
|
|
|
|
|
|
template<class TUP>
|
2025-06-23 23:02:15 +02:00
|
|
|
concept tuple_accessible =
|
|
|
|
|
tuple_sized<TUP> and
|
|
|
|
|
WithIdxSeq<std::tuple_size_v<TUP>>::andAll([](auto idx)
|
|
|
|
|
{
|
|
|
|
|
return tuple_element_accessible<TUP,idx>;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class TUP>
|
|
|
|
|
concept tuple_like = not is_reference_v<TUP>
|
|
|
|
|
and tuple_sized<remove_cv_t<TUP>>
|
|
|
|
|
and tuple_accessible<remove_cv_t<TUP>>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<std::size_t idx, class TUP>
|
|
|
|
|
requires(tuple_like<std::remove_reference_t<TUP>>)
|
|
|
|
|
decltype(auto)
|
|
|
|
|
get (TUP&& tup)
|
|
|
|
|
{
|
|
|
|
|
using Tup = std::remove_reference_t<TUP>;
|
|
|
|
|
static_assert (0 < std::tuple_size_v<Tup>);
|
|
|
|
|
if constexpr (tuple_mem_accessible<Tup,0>)
|
|
|
|
|
{
|
|
|
|
|
if constexpr (std::is_reference_v<TUP>)
|
|
|
|
|
return tup.template get<idx>();
|
|
|
|
|
else
|
|
|
|
|
{ // return value copy when tuple given as RValue
|
|
|
|
|
using Elm = std::tuple_element_t<idx, TUP>;
|
|
|
|
|
Elm elm(tup.template get<idx>());
|
|
|
|
|
return elm;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
using std::get;
|
|
|
|
|
return get<idx> (std::forward<TUP> (tup));
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-22 19:42:03 +02:00
|
|
|
|
|
|
|
|
}}//namespace lib::meta
|
2024-12-12 04:38:55 +01:00
|
|
|
|
|
|
|
|
|
clean-up: simplify function-closure -- avoiding `std::function`
A very performance relevant shortcoming of the existing implementation
of partial function closure is that the result is always wrapped into a
std::function, which typically causes a heap allocation when more than
a single pre-bound argument must be stored — which is annoying,
since the underlying Binder provides inline storage and thus
could be handled directly as a value object.
However, returning the Binder directly is also problematic, since
this object is outfitted with several overloaded function call operators,
which defeats most techniques to detect a function signature. Notably,
relevant down-stream metaprogramming code, like the tuple-closure used
in the `NodeBuilder` would break when being confronted directly with
a binder object.
An investigation shows that there is no direct remedy, short of
wrapping the binder into another functor. This can be accomplished
with a helper template, that generates a wrapper; however, this
wrapper builder must be supplied with explicit type information
regarding the function arguments (precisely because this type
signature can not be picked up from the Binder object itself)
2025-06-06 19:44:24 +02:00
|
|
|
|
2025-06-22 19:42:03 +02:00
|
|
|
template<typename X>
|
clean-up: simplify function-closure -- avoiding `std::function`
A very performance relevant shortcoming of the existing implementation
of partial function closure is that the result is always wrapped into a
std::function, which typically causes a heap allocation when more than
a single pre-bound argument must be stored — which is annoying,
since the underlying Binder provides inline storage and thus
could be handled directly as a value object.
However, returning the Binder directly is also problematic, since
this object is outfitted with several overloaded function call operators,
which defeats most techniques to detect a function signature. Notably,
relevant down-stream metaprogramming code, like the tuple-closure used
in the `NodeBuilder` would break when being confronted directly with
a binder object.
An investigation shows that there is no direct remedy, short of
wrapping the binder into another functor. This can be accomplished
with a helper template, that generates a wrapper; however, this
wrapper builder must be supplied with explicit type information
regarding the function arguments (precisely because this type
signature can not be picked up from the Binder object itself)
2025-06-06 19:44:24 +02:00
|
|
|
void
|
2025-06-22 19:42:03 +02:00
|
|
|
show()
|
Buffer-Provider: investigate Problem with embedded type-constructor-arguments
This is a possible extension which frequently comes up again during the design of the Engine.
Basically, the `TypeHandler` in the metadata-descriptor used by the `BufferProvder` could capture
additional context-arguments, which are then later passed to an object instance embedded into the buffer.
Yesterday I attempted to use this feature for a simple demonstration in `NodeBasic_test`,
just to find out that passing additional constructor arguments to the capture fails with
a confusing compilation error message. This failure could be traced down to the function binder;
and what at first sight seemed to be a compiler error, turned out to be a quite logical limitation:
When we »close« some objects of the constructor, but delay the construction itself, we'll have to
store a copy in the constructor-λ. And this implies, that we'll have to change the types
used for instantiation of the compiler, so that the construction-function can be invoked
by passing references from the captured copy of the additional arguments.
When naively passing those forwarded arguments into the std::bind()-call,
the resulting functor will fail at instantiation, when the compiler attempts
to generate the function-call `operator()`
see: https://stackoverflow.com/q/30968573/444796
2024-12-16 23:01:57 +01:00
|
|
|
{
|
2025-06-22 19:42:03 +02:00
|
|
|
SHOW_TYPE(X)
|
Buffer-Provider: investigate Problem with embedded type-constructor-arguments
This is a possible extension which frequently comes up again during the design of the Engine.
Basically, the `TypeHandler` in the metadata-descriptor used by the `BufferProvder` could capture
additional context-arguments, which are then later passed to an object instance embedded into the buffer.
Yesterday I attempted to use this feature for a simple demonstration in `NodeBasic_test`,
just to find out that passing additional constructor arguments to the capture fails with
a confusing compilation error message. This failure could be traced down to the function binder;
and what at first sight seemed to be a compiler error, turned out to be a quite logical limitation:
When we »close« some objects of the constructor, but delay the construction itself, we'll have to
store a copy in the constructor-λ. And this implies, that we'll have to change the types
used for instantiation of the compiler, so that the construction-function can be invoked
by passing references from the captured copy of the additional arguments.
When naively passing those forwarded arguments into the std::bind()-call,
the resulting functor will fail at instantiation, when the compiler attempts
to generate the function-call `operator()`
see: https://stackoverflow.com/q/30968573/444796
2024-12-16 23:01:57 +01:00
|
|
|
}
|
2024-12-12 04:38:55 +01:00
|
|
|
|
2025-06-22 19:42:03 +02:00
|
|
|
template<lib::meta::tuple_like X>
|
|
|
|
|
void
|
|
|
|
|
show()
|
clean-up: simplify function-closure -- avoiding `std::function`
A very performance relevant shortcoming of the existing implementation
of partial function closure is that the result is always wrapped into a
std::function, which typically causes a heap allocation when more than
a single pre-bound argument must be stored — which is annoying,
since the underlying Binder provides inline storage and thus
could be handled directly as a value object.
However, returning the Binder directly is also problematic, since
this object is outfitted with several overloaded function call operators,
which defeats most techniques to detect a function signature. Notably,
relevant down-stream metaprogramming code, like the tuple-closure used
in the `NodeBuilder` would break when being confronted directly with
a binder object.
An investigation shows that there is no direct remedy, short of
wrapping the binder into another functor. This can be accomplished
with a helper template, that generates a wrapper; however, this
wrapper builder must be supplied with explicit type information
regarding the function arguments (precisely because this type
signature can not be picked up from the Binder object itself)
2025-06-06 19:44:24 +02:00
|
|
|
{
|
2025-06-22 19:42:03 +02:00
|
|
|
cout << "Tup!! "<< lib::test::showType<X>() <<endl;
|
|
|
|
|
lib::meta::forEachIDX<X> ([](auto i)
|
|
|
|
|
{
|
|
|
|
|
using Elm = std::tuple_element_t<i, X>;
|
|
|
|
|
cout <<" "<<uint(i)<<": "<< lib::test::showType<Elm>() <<endl;
|
|
|
|
|
});
|
Buffer-Provider: investigate Problem with embedded type-constructor-arguments
This is a possible extension which frequently comes up again during the design of the Engine.
Basically, the `TypeHandler` in the metadata-descriptor used by the `BufferProvder` could capture
additional context-arguments, which are then later passed to an object instance embedded into the buffer.
Yesterday I attempted to use this feature for a simple demonstration in `NodeBasic_test`,
just to find out that passing additional constructor arguments to the capture fails with
a confusing compilation error message. This failure could be traced down to the function binder;
and what at first sight seemed to be a compiler error, turned out to be a quite logical limitation:
When we »close« some objects of the constructor, but delay the construction itself, we'll have to
store a copy in the constructor-λ. And this implies, that we'll have to change the types
used for instantiation of the compiler, so that the construction-function can be invoked
by passing references from the captured copy of the additional arguments.
When naively passing those forwarded arguments into the std::bind()-call,
the resulting functor will fail at instantiation, when the compiler attempts
to generate the function-call `operator()`
see: https://stackoverflow.com/q/30968573/444796
2024-12-16 23:01:57 +01:00
|
|
|
}
|
2024-12-12 15:55:02 +01:00
|
|
|
|
|
|
|
|
|
2016-01-05 03:32:24 +01:00
|
|
|
int
|
|
|
|
|
main (int, char**)
|
|
|
|
|
{
|
2025-06-23 23:02:15 +02:00
|
|
|
using Tup = std::tuple<long,short>;
|
2025-06-22 19:42:03 +02:00
|
|
|
using Arr = std::array<int,3>;
|
|
|
|
|
using Hetero = lib::HeteroData<int,string>::Chain<short>::ChainExtent<bool,lib::meta::Nil>::ChainType;
|
2023-11-20 02:00:56 +01:00
|
|
|
|
2025-06-22 19:42:03 +02:00
|
|
|
SHOW_EXPR((lib::meta::tuple_sized<Tup> ))
|
|
|
|
|
SHOW_EXPR((lib::meta::tuple_sized<Arr> ))
|
|
|
|
|
SHOW_EXPR((lib::meta::tuple_sized<Hetero> ))
|
|
|
|
|
SHOW_EXPR((lib::meta::tuple_sized<int> ))
|
clean-up: simplify function-closure -- avoiding `std::function`
A very performance relevant shortcoming of the existing implementation
of partial function closure is that the result is always wrapped into a
std::function, which typically causes a heap allocation when more than
a single pre-bound argument must be stored — which is annoying,
since the underlying Binder provides inline storage and thus
could be handled directly as a value object.
However, returning the Binder directly is also problematic, since
this object is outfitted with several overloaded function call operators,
which defeats most techniques to detect a function signature. Notably,
relevant down-stream metaprogramming code, like the tuple-closure used
in the `NodeBuilder` would break when being confronted directly with
a binder object.
An investigation shows that there is no direct remedy, short of
wrapping the binder into another functor. This can be accomplished
with a helper template, that generates a wrapper; however, this
wrapper builder must be supplied with explicit type information
regarding the function arguments (precisely because this type
signature can not be picked up from the Binder object itself)
2025-06-06 19:44:24 +02:00
|
|
|
|
2025-06-23 23:02:15 +02:00
|
|
|
SHOW_EXPR((lib::meta::tuple_element_accessible<Tup,0>))
|
|
|
|
|
// SHOW_EXPR((lib::meta::tuple_element_accessible<Tup,2>))
|
|
|
|
|
SHOW_EXPR((lib::meta::tuple_element_accessible<Hetero,0>))
|
|
|
|
|
SHOW_EXPR((lib::meta::tuple_accessible<Tup>))
|
|
|
|
|
SHOW_EXPR((lib::meta::tuple_accessible<Hetero>))
|
clean-up: simplify function-closure -- avoiding `std::function`
A very performance relevant shortcoming of the existing implementation
of partial function closure is that the result is always wrapped into a
std::function, which typically causes a heap allocation when more than
a single pre-bound argument must be stored — which is annoying,
since the underlying Binder provides inline storage and thus
could be handled directly as a value object.
However, returning the Binder directly is also problematic, since
this object is outfitted with several overloaded function call operators,
which defeats most techniques to detect a function signature. Notably,
relevant down-stream metaprogramming code, like the tuple-closure used
in the `NodeBuilder` would break when being confronted directly with
a binder object.
An investigation shows that there is no direct remedy, short of
wrapping the binder into another functor. This can be accomplished
with a helper template, that generates a wrapper; however, this
wrapper builder must be supplied with explicit type information
regarding the function arguments (precisely because this type
signature can not be picked up from the Binder object itself)
2025-06-06 19:44:24 +02:00
|
|
|
|
2025-06-22 19:42:03 +02:00
|
|
|
SHOW_EXPR((lib::meta::tuple_like<Tup> ))
|
|
|
|
|
SHOW_EXPR((lib::meta::tuple_like<Arr> ))
|
|
|
|
|
SHOW_EXPR((lib::meta::tuple_like<Hetero> ))
|
|
|
|
|
SHOW_EXPR((lib::meta::tuple_like<int> ))
|
clean-up: simplify function-closure -- avoiding `std::function`
A very performance relevant shortcoming of the existing implementation
of partial function closure is that the result is always wrapped into a
std::function, which typically causes a heap allocation when more than
a single pre-bound argument must be stored — which is annoying,
since the underlying Binder provides inline storage and thus
could be handled directly as a value object.
However, returning the Binder directly is also problematic, since
this object is outfitted with several overloaded function call operators,
which defeats most techniques to detect a function signature. Notably,
relevant down-stream metaprogramming code, like the tuple-closure used
in the `NodeBuilder` would break when being confronted directly with
a binder object.
An investigation shows that there is no direct remedy, short of
wrapping the binder into another functor. This can be accomplished
with a helper template, that generates a wrapper; however, this
wrapper builder must be supplied with explicit type information
regarding the function arguments (precisely because this type
signature can not be picked up from the Binder object itself)
2025-06-06 19:44:24 +02:00
|
|
|
|
2025-06-22 19:42:03 +02:00
|
|
|
show<Tup>();
|
|
|
|
|
show<Arr>();
|
|
|
|
|
show<Hetero>();
|
|
|
|
|
show<int>();
|
2024-12-12 15:55:02 +01:00
|
|
|
|
2025-06-23 23:02:15 +02:00
|
|
|
SHOW_EXPR((std::tuple_size_v<const Tup>))
|
|
|
|
|
using Elm1 = std::tuple_element_t<1, const Tup>;
|
|
|
|
|
SHOW_TYPE(Elm1)
|
|
|
|
|
using TupConstSeq = lib::meta::ElmTypes<const Tup>::Seq;
|
|
|
|
|
SHOW_TYPE(TupConstSeq)
|
|
|
|
|
|
|
|
|
|
using T1 = decltype(lib::meta::get<0> (std::declval<Tup>()));
|
|
|
|
|
SHOW_TYPE(T1)
|
|
|
|
|
using T2 = decltype(lib::meta::get<0> (std::declval<Tup&>()));
|
|
|
|
|
SHOW_TYPE(T2)
|
|
|
|
|
using T3 = decltype(lib::meta::get<0> (std::declval<Tup const&>()));
|
|
|
|
|
SHOW_TYPE(T3)
|
|
|
|
|
|
|
|
|
|
using H1 = decltype(lib::meta::get<4> (std::declval<Hetero>()));
|
|
|
|
|
SHOW_TYPE(H1)
|
|
|
|
|
using H2 = decltype(lib::meta::get<4> (std::declval<Hetero&>()));
|
|
|
|
|
SHOW_TYPE(H2)
|
|
|
|
|
using H3 = decltype(lib::meta::get<4> (std::declval<Hetero const&>()));
|
|
|
|
|
SHOW_TYPE(H3)
|
2025-06-23 04:23:55 +02:00
|
|
|
|
Buffer-Provider: investigate Problem with embedded type-constructor-arguments
This is a possible extension which frequently comes up again during the design of the Engine.
Basically, the `TypeHandler` in the metadata-descriptor used by the `BufferProvder` could capture
additional context-arguments, which are then later passed to an object instance embedded into the buffer.
Yesterday I attempted to use this feature for a simple demonstration in `NodeBasic_test`,
just to find out that passing additional constructor arguments to the capture fails with
a confusing compilation error message. This failure could be traced down to the function binder;
and what at first sight seemed to be a compiler error, turned out to be a quite logical limitation:
When we »close« some objects of the constructor, but delay the construction itself, we'll have to
store a copy in the constructor-λ. And this implies, that we'll have to change the types
used for instantiation of the compiler, so that the construction-function can be invoked
by passing references from the captured copy of the additional arguments.
When naively passing those forwarded arguments into the std::bind()-call,
the resulting functor will fail at instantiation, when the compiler attempts
to generate the function-call `operator()`
see: https://stackoverflow.com/q/30968573/444796
2024-12-16 23:01:57 +01:00
|
|
|
cout << "\n.gulp." <<endl;
|
2007-08-17 00:36:07 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|