0

I am working on a C++ function template and I am trying to find a way to access the function arguments within the template. Specifically, I have a template function called enumerate that takes a callable object as an argument.

※ However, I am unable to modify the generated code for the enumerate function.

Here is an example of the enumerate function:


template <typename F> void enumerate(F &func) noexcept(false) {
    vector<int> a{1, 2, 3, 4};
    int b = 10;
    func(a);
    func(b);
}

example code could be

#include <iostream>
#include <vector>

using namespace std;

template <typename T> void func(const T &data) {
    cout << "hello " << typeid(T).name() << endl;
}
template <typename F> void enumerate(F &func) noexcept(false) {
    vector<int> a{1, 2, 3, 4};
    int b = 10;
    func(a);
    func(b);
}

int main() {
    enumerate(func);
    return 0;
}

I want to be able to handle different types of arguments, such as vectors and integers, within the func callable object. How can I achieve this without modifying the generated code for the enumerate function? Are there any techniques or approaches that I can use to access and process the function arguments within the function template?

Any help or guidance would be greatly appreciated. Thank you in advance.

below code could not deduce template parameter from compile side.

template <typename T> void func(const T &data) {
    cout << "hello " << typeid(T).name() << endl;

}
4
  • 4
    Wrap function with lambda like auto lambda = [](const auto& data) { func(data); }; enumerate(lambda);? Commented Jan 16, 2024 at 6:44
  • 2
    Few options : partial template specialization or use if constexpr with traits like std::is_same_v. Whatever you do do NOT use typeid(T) that is a runtime thing. Commented Jan 16, 2024 at 7:02
  • "I am trying to find a way to access the function arguments within the template." -- "I want to be able to handle different types of arguments," -- These are rather different goals. The former is trivial -- the argument to func is passed in as data, so to access the argument, you use data. You should re-write your question to focus on the second formulation of your goal. Commented Jan 16, 2024 at 7:35
  • 1
    Looks like XY problem. And also seems unclear to me. Commented Jan 16, 2024 at 8:13

2 Answers 2

1

You can pass a generic lambda instead of using a function template:

#include <iostream>
#include <vector>

template <typename F>
void enumerate(F func) {
    std::vector<int> a{1, 2, 3, 4};
    int b = 10;
    func(a);
    func(b);
}

int main() {
    enumerate([](const auto& data) {
        using T = std::remove_cvref_t<decltype(data)>;
        std::cout << "hello " << typeid(T).name() << std::endl;
    });
}

Unlike the function template, this works because you are passing an object of some non-templated closure type. The call operator of this closure type is a template, which allows you to pass objects of different types.

Sign up to request clarification or add additional context in comments.

Comments

1

As Jan Schultke already demonstrated, you can use a generic lambda but more generally, any functor will do (see 3 examples below, a traditional functor, a generic lambda so basically replicating Jan's for completeness, and a lambda template if C++20 or later). Also, I've #included "TypeTraits.h" in the code below from here (I'm the author) in order to get the user friendly name of your type at compile time (using the "TypeName_v" template seen below). Everything's fully documented at the latter link. However, it's unclear to me exactly what your requirements are so I'm not even sure if you need to pass "func" or not (if you're really just after a way to display the type name of an arbitrary type using a single format only so unclear if "func" is really required). "TypeName_v" will give you the name but the library provides easy-to-use (syntactically very clean) functionality to process function argument types in general (that's its purposes).

Lastly, I've left things as you coded them, with the functors taking a "const T &data" arg and "enumerator()" taking an "F &" arg, though you may want to review this, possibly to take forward referencing args but it's an unrelated topic (you currently can't pass a temporary to "enumerate()" for instance, since you'll get an error trying to bind an rvalue to a non-const lvalue reference).

Click here to run it:

#include <iostream>
#include <vector>
#include <string_view>

using namespace std;

///////////////////////////////////////////////////////////
// See https://github.com/HexadigmSystems/FunctionTraits.
// #including "TypeTraits.h" here (library is effectively
// a single-header) to pick up "TypeName_v" seen below
// (or anything else in the header that might help you
// but just "TypeName_v") for now):
///////////////////////////////////////////////////////////
#include "TypeTraits.h"
using namespace StdExt; // Everything in above header in this namespace

/////////////////////////////////////////////////
// See "TypeName_v" in above link. Returns the
// user friendly-name of type "T" but the
// following also removes the reference from "T"
// as well if present, and any cv-qualifiers
// as well, usually "const" (likely how you prefer
// it in the situation below, otherwise "&",
// "const", etc. might appear in the name depending
// on how things are coded below but longer story).
// Note BTW that "std::remove_cvref_t" in the
// following is a C++20 feature but you can roll
// your own of course in earlier versions.
/////////////////////////////////////////////////
template <typename T>
inline constexpr auto TypeNameWithCvRefRemoved_v = TypeName_v<std::remove_cvref_t<T>>;

// Functor
struct func
{
    template <typename T>
    void operator()(const T &data) const
    {
        cout << "Your functor: " << TypeNameWithCvRefRemoved_v<T> << endl;
    }
};

template <typename F>
void enumerate(F &func) noexcept(false)
{
    vector<int> a{1, 2, 3, 4};
    int b = 10;
    func(a);
    func(b);
}

int main()
{
    // Traditional functor
    func f1;
    enumerate(f1);

    // Generic lambda
    auto f2 = [](const auto &data)
                {
                    std::cout << "Generic lambda: " << TypeNameWithCvRefRemoved_v<decltype(data)> << std::endl;
                };
    enumerate(f2);

    
    // Lambda template (C++20 or later)
    auto f3 = []<typename T>(const T &data)
                            {
                                std::cout << "Lambda template: " << TypeNameWithCvRefRemoved_v<T> << std::endl;
                            };
    enumerate(f3);

    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.