LUMIERA.clone/research/try.cpp
Ichthyostega 20392eee1c clean-up: successfully replaced the old fixed type sequence (closes: #987)
This resolves an intricate problem related to metaprogramming with
variadic templates and function signatures. Due to exceptional complexity,
a direct solution was blocked for several years, and required a better
organisation of the support code involved; several workarounds were
developed, gradually leading to a transition path, which could now
be completed in an focused clean-up effort over the last week.

Metaprogramming with sequences of types is organised into three layers:
- simple tasks can be solved with the standard facilities of the language,
  using pattern match with variadic template specialisations
- the ''type-sequence'' construct `Types<T...>` takes the centre stage
  for the explicit definition of collections of types; it can be re-bound
  to other variadic templates and supports simple direct manipulation
- for more elaborate and advanced processing tasks, a ''Loki-style type list''
  can be obtained from a type-sequence, allowing to perform recursive
  list processing task with a technique similar to LISP.
2025-06-07 18:04:59 +02:00

139 lines
4.4 KiB
C++

/* try.cpp - to try out and experiment with new features....
* scons will create the binary bin/try
*/
// 06/25 - investigate function type detection of std::bind Binders
// 12/24 - investigate problem when perfect-forwarding into a binder
// 12/24 - investigate overload resolution on a templated function similar to std::get
// 11/24 - how to define a bare object location comparison predicate
// 11/23 - prototype for grouping from iterator
/** @file try.cpp
* Investigate ambiguities regarding the function type of standard binders.
* The Binder objects returned from `std::bind` provide a set of overloaded
* function call `operator()` (with variants for c/v). Unfortunately this defeats
* the common techniques to detect a function signature from a callable, when
* only a concrete instance of such a binder is given. Furthermore, looking
* at the definition of `class _Bind_result<_Result, _Functor(_Bound_args...)>`
* in my implementation of the C++ Stdlib, it seems we are pretty much out
* of luck, and even `std::function` fails with the template argument detection.
* A possible workaround could be to wrap the Binder object immediately into
* a lambda, but only if the actual types for the argument list can be
* provided directly to a template to generate this λ-wrapper.
* @note there is a nasty twist regarding const-correctness, which almost made
* this workaround fail altogether. The overloaded operator() from std::bind
* serves the same purpose (to deal with const/volatile), and this is the
* very reason that defeats the detection of the function signature.
* The workaround attempts to expose precisely one function call operator,
* and this becomes problematic as soon as the resulting object is processed
* further, and maybe bound into another lambda capture. Thus we define the
* wrapper class explicitly, so that any const-ness can be cast away.
* This turns out to be necessary in tuple-closure.hpp.
*/
#include "lib/format-cout.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/test/diagnostic-output.hpp"
#include "lib/meta/function.hpp"
#include "lib/meta/variadic-rebind.hpp"
#include "lib/util.hpp"
#include <functional>
using std::forward;
using std::placeholders::_1;
using lib::meta::_Fun;
void
fun (int& a)
{
std::cout << a << std::endl;
}
short
fup (long l, long long ll)
{
return short(l - ll);
}
/** WORKAROUND: wrap a binder to yield clear function signature */
template<typename...ARGS>
struct AdaptInvokable
{
template<class FUN>
static auto
buildWrapper (FUN&& fun)
{
struct Wrap
{
FUN fun_;
Wrap(FUN&& f) : fun_{forward<FUN>(f)} { }
auto
operator() (ARGS... args)
{
return fun_(forward<ARGS>(args)...);
}
};
return Wrap{forward<FUN>(fun)};
///////////////////////////////////////////////////////////// NOTE
///////////////////////////////////////////////////////////// can not use a Lambda, since we're then trapped
///////////////////////////////////////////////////////////// in an unsurmountable mixture of const and non-const
// return [functor = forward<FUN>(fun)]
// (ARGS... args) mutable
// {
// return functor (forward<ARGS> (args)...);
// };
}
};
template<class TYPES, class FUN>
auto
buildInvokableWrapper (FUN&& fun)
{
using ArgTypes = typename TYPES::Seq;
using Builder = typename lib::meta::RebindVariadic<AdaptInvokable, ArgTypes>::Type;
return Builder::buildWrapper (forward<FUN> (fun));
}
int
main (int, char**)
{
SHOW_EXPR (fup(2,3))
auto bup = std::bind (fup, _1, 5);
SHOW_EXPR (bup)
using Bup = decltype(bup);
using Fub = _Fun<Bup>;
SHOW_TYPE (Bup)
SHOW_TYPE (Fub)
// using Sub = Fub::Sig; ////////////////Problem: does not compile
using Fut = decltype(fun);
SHOW_TYPE (_Fun<Fut>::Sig)
auto wup = buildInvokableWrapper<lib::meta::Types<int>>(bup);
using Wup = decltype(wup);
using WupSig = _Fun<Wup>::Sig;
SHOW_TYPE (WupSig);
SHOW_EXPR (wup(3))
SHOW_EXPR (sizeof(bup))
SHOW_EXPR (sizeof(wup))
auto waua = buildInvokableWrapper<lib::meta::Types<>> (std::bind (fun, 55));
waua ();
SHOW_TYPE (_Fun<decltype(waua)>::Sig)
cout << "\n.gulp." <<endl;
return 0;
}