Vitaly Protasov
Vitaly Protasov

Reputation: 179

deducing general lambdas argument type

I wish to pass void and single argumented direct lambdas to operator call

class My { public: template<typename Callable> operator >> (Callable &&callback){ callback(); return *this;}  };

When pass lambda with auto like

My a; a >> ([](){ std::cout << "void lambda called" });

and check inner variant for monostate, it works, calls my lambda,

but I don't know how to extract type from lambda argument to call std::visit for passed to operator lambda since there need to be a check for what type is currently sits in variant, only working for compile in MSVC 2022 solution for C++20 that call all provided lambda independently what type is in variant, and it starts fails if add monostate to typelist.

template <typename... Ts>
class Test {
public:
    Test(const std::variant<Ts...>& v) : my(v) {}
    template <typename Callable>
    auto operator|(Callable&& c) {
        // this will visit for all types (outut int = 6 bool = 1) but should just for int?
        std::visit([&](const auto& val) { std::invoke(c, val); }, std::as_const(my));

        return *this;

    }
private:
    std::variant<Ts...> my;
};
int main() {
    std::variant<bool, int> test_var = 6;

    // Create a Test object using the variant
    Test a(test_var);
    int x = 2;
    // Use the operator| on the Test object, not directly on the variant
    a | [&](const int& what_value) { std::cout << "int " << (what_value + x) << '\n'; };
    a | [](const bool& bool_is) { std::cout << bool_is << '\n'; };
    //a | [](const std::monostate& mono) { std::cout << "mono"; };
    return 0;
}

If I create object that hold type T and that lambda I can make this works fine even for monostate listed in variant or if it's in monostate value, then passed visiting lambdas won't be executed, but syntax like `std::variant<std::monostate, float, int> a; Test(a) | on_type([](const auto &flt){ std::cout << flt; };

is long, like idea to wrap that call and pass for on_type object lambda holder that do all job somehow by deducing what arument type instead of auto passed Test(a) |[](const float &a){ std::cout << a; } // as this.

broke head about 2 days and still no luck.

Upvotes: 0

Views: 157

Answers (1)

Larry
Larry

Reputation: 968

I don't follow exactly what you're trying to do. Are you saying "operator>>" should take a lambda with either no args or just a single arg only? If a single arg, then what should "operator>>" pass for that arg when it invokes "callback"? As for getting the argument type, you can use my FunctionTraits library rather than try to roll your own (library is effectively single header only, free and fully documented).

The code below is very crude (quick and dirty) but shows you what's possible as it pertains to your particular issue. You'll (very) likely need to tailor it for your needs however which isn't completely clear to me (but the library now provides you with a full-scale tool if you need it).

Click here to run it:

#include <iostream>

 // See https://github.com/HexadigmSystems/FunctionTraits
#include "TypeTraits.h"

// Everything in above header in this namespace
using namespace StdExt;

class My
{
public:

    template<typename Callable>
    My& operator >> (Callable &&callback)
    {
        // "Callable" takes no args?
        if constexpr (IsEmptyArgList_v<Callable>)
        {
            callback();
        }
        /////////////////////////////////////////
        // "Callable" takes 1 arg (also checking
        // to make sure it's not variadic, i.e.,
        // "..." doesn't follow the 1st arg in
        // this case)
        /////////////////////////////////////////
        else if constexpr (ArgCount_v<Callable> == 1 && // Arg count (i.e., arity)
                           !IsVariadic_v<Callable>) // No "..." at the end
        {
            //////////////////////////////////////
            // Type of the 1st arg of "Callable"
            // (there's only one arg from check
            // above)
            //////////////////////////////////////
            using Type = ArgType_t<Callable, 0>;

            ///////////////////////////////////////
            // Ok, you've got the type, now what?
            // (what do you pass to "callback").
            // We'll just pass "Type" for now
            // (default constructed)
            ///////////////////////////////////////
            callback(Type());
        }
        else
        {
            // See "AlwaysFalse" in "TypeTraits.h"
            static_assert(AlwaysFalse<Callable>, "\"Callable\" must take zero or 1 arg only");
        }
         
         return *this;
    }

    template<typename Callable>
    My& operator << (Callable &&callback)
    {
        //////////////////////////////////////
        // Assumes "Callable" takes at least
        // 1 arg but see code above for how
        // to check the number of args
        //////////////////////////////////////
        using Type = ArgType_t<Callable, 0>;

        // Display the type (just to show you how)
        tcout << TypeName_v<Type> << _T("\n");

        //callback(val); // From your code

        return *this;
    }
};    

int main()
{
    My a1;
    a1 >> ([]()
          {
              tcout << _T("void lambda with 0 args called\n");
          });

    My a2;
    a2 >> ([](int x)
          {
              tcout << _T("void lambda with 1 arg called (") << x << _T(")\n");
          });          

    My b;
    b << ([](float x)
          {
              tcout << x;
          });

    return 0;
}

Upvotes: 1

Related Questions