Search code examples
c++argumentsc++14voidtemplate-specialization

Skip void arguments


I'm trying to call a function with the correct number of parameters in C++14. I have a template class that defines an alias of the itself or void, based on template filters : either the argument is skipped, or passed, something like this :

template<typename comp>
struct exclude {};

template<typename comp>
struct shouldPassComponent
{
    using type = comp;
    
    comp& operator()(comp* component) { return *component; }
    const comp& operator()(const comp* component) const { return *component; }
}

// void is aliased instead of the component
template<typename comp>
struct shouldPassComponent<exclude<comp>>
{
    using type = void;

    void operator()(comp* component) {}
    void operator()(const comp* component) const {}
}

// if void, the argument should be skipped/not evaluated instead
std::invoke(func, shouldPassComponent<types>()(comps)...); // error here

Unfortunately it doesn't work because the compiler still evaluates "void()" in arguments (error: "no matching overloaded function found").

So I tried the non-template way to see if it would work :

void CallFunction();

CallFunction(void()); // error here

However, the compiler errors with : "error C2672: CallFunction: no matching overloaded function found". So I thought about the fact that lambda accepts auto arguments :

void CallFunction();

auto lambdaTest = [](auto... Arguments)
{
    //....
    CallFunction(Arguments...);
};

lambdaTest(void()); // error here

Still, the compiler errors with : "error C2672: 'operator __surrogate_func': no matching overloaded function found" when calling lambdaTest. I searched for hours on Internet, and now I'm out of luck.

Is there any way to prevent evaluating/discarding some arguments from a variadic to be passed? Any solution will be appreciated.


Solution

  • I found a solution : https://stackoverflow.com/a/36818808/9142528 it's based on an index sequence which I never thought about. It feeds an index sequence based on a predictate (for each type, puts the index of the type if the predicate for this type has a value of true, nothing otherwise).

    template<typename component>
    struct exclude {};
    
    template<typename component>
    struct isResolvable
    {
        enum { value = true };
    };
    
    template<typename component>
    struct isResolvable<exclude<component>>
    {
        enum { value = false };
    };
    
    template<typename...components>
    struct view_t
    {
        template<typename component>
        struct component_t
        {
            using type = component;
        };
    
        template<typename component>
        struct component_t<exclude<component>>
        {
            using type = component;
        };
    
        template<typename funcType>
        static void call(funcType func, typename component_t<components>::type&... comps)
        {
            callImpl(func, std::make_tuple((&comps)...), find_indices<isResolvable, components...>{});
        }
    
        template<typename funcType, typename...components, size_t...Is>
        static void callImpl(funcType func, const std::tuple<components*...>& tuple, std::index_sequence<Is...>)
        {
            std::invoke(func, *std::get<Is>(tuple)...);
        }
    };
    
    struct test1 {};
    struct test2 {};
    
    void testCallback(test1& comp)
    {
    }
    
    void test()
    {
        test1 comp1;
        test2 comp2;
    
        view_t<test1, exclude<test2>>::call(&testCallback, comp1, comp2);
    }