Reputation: 13
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;
}
Upvotes: 0
Views: 139
Reputation: 968
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;
}
Upvotes: 1
Reputation: 39804
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.
Upvotes: 1