Instantiating an object with a function pointer, with its arguments filtered by type

1 day ago 1
ARTICLE AD BOX

The Deduction Failure

In your addPattern, you have:
void addPattern(void(*view)(TypeList<Filter<...>>), Types ...args)

The compiler tries to determine what Types... is by looking at both the function pointer (view) and the arguments (args...).

From args..., it sees const char* and int.

From view, it sees a complex instruction: "The result of filtering Types... must be int."

C++ deduction is not an equation solver. Because it can't be 100% sure what Types... should be based on the function pointer, deduction fails.

The TypeList Expansion Issue

Your code uses TypeList<int> as a parameter type. To the compiler, TypeList<int> is a single struct type, not a list of arguments.

What you wanted: void(int)

What you wrote: void(TypeList<int>)

These are incompatible. A function expecting one integer cannot accept a TypeList object.

Here is a bit longish solution + added invoker:

#include <iostream> #include <string> #include <tuple> #include <type_traits> #include <utility> // Type Filtering Logic template<typename... Ts> struct TypeList {}; template<typename In, template<typename> class Pred, typename Out = TypeList<>> struct Filter; template<template<typename> class Pred, typename... Out> struct Filter<TypeList<>, Pred, TypeList<Out...>> { using type = TypeList<Out...>; }; //recursive engine of the filter template<typename T, typename... Rest, template<typename> class Pred, typename... Out> struct Filter<TypeList<T, Rest...>, Pred, TypeList<Out...>> { using type = typename std::conditional_t<Pred<T>::value, Filter<TypeList<Rest...>, Pred, TypeList<Out..., T>>, Filter<TypeList<Rest...>, Pred, TypeList<Out...>>>::type; }; template<typename T> struct MakeFuncPtr; template<typename... Args> struct MakeFuncPtr<TypeList<Args...>> { using type = void(*)(Args...); }; template<typename... AllArgs> class ViewPath { using FilteredTypes = typename Filter<TypeList<AllArgs...>, std::is_integral>::type; using FuncPtr = typename MakeFuncPtr<FilteredTypes>::type; FuncPtr view_func; std::tuple<AllArgs...> view_args; // This helper returns a 1-element tuple if T is integral, otherwise an empty tuple. template<typename T> auto wrap_if_integral(T&& val) { if constexpr (std::is_integral_v<std::decay_t<T>>) { return std::make_tuple(std::forward<T>(val)); } else { return std::tuple<>{}; } } template<size_t... Is> void invoke_helper(std::index_sequence<Is...>) { // tuple_cat joins all the 1-element and 0-element tuples into one flat list auto filtered_args = std::tuple_cat(wrap_if_integral(std::get<Is>(view_args))...); std::apply(view_func, filtered_args); } public: ViewPath(FuncPtr v, AllArgs... args) : view_func(v), view_args(std::make_tuple(std::move(args)...)) {} void call() { invoke_helper(std::index_sequence_for<AllArgs...>{}); } }; template<typename... Types> void addPattern(typename MakeFuncPtr<typename Filter<TypeList<Types...>, std::is_integral>::type>::type view, Types... args) { ViewPath<Types...> vp(view, std::move(args)...); vp.call(); } void testSingle(int a) { std::cout << "Single: " << a << "\n"; } void testMulti(int a, int b) { std::cout << "Sum: " << (a + b) << "\n"; } void testMixed(int i, char c) { std::cout << "Int: " << i << ", Char: " << c << "\n"; } int main() { addPattern(testSingle, "Ignore me", 73); addPattern(testMulti, 10, std::string("Middle"), 20); addPattern(testMixed, 500, "Extra", 'A'); return 0; }

https://godbolt.org/z/hd81baeTf

Notes:

std::is_integral_v<char> is true. So 'A' passes the filter and reaches testMixed as expected here — but so would bool, short, long etc. If the intent is "only int", the predicate should be:

template<typename T> struct IsInt : std::is_same<std::decay_t<T>, int> {};
Read Entire Article