My template-fu is rather weak. I have this code:
template<typename T>
void Foo(void(*func)(T*)) { }
void Callback(int* data) { }
int Test()
{
Foo(Callback);
}
...but I'd like something more readable than C's nasty function pointer syntax of void(*func)(T*).
Someone on my team suggested this:
template<typename T>
struct Types
{
typedef void Func(T*);
};
template<typename T>
void Foo2(typename Types<T>::Func* func) {}
void Test2()
{
Foo2(Callback); // could not deduce template argument for 'T'
Foo2<int>(Callback); // ok
}
(I'm still debating whether this is actually more readable, but that's a separate issue.)
How can I help the compiler figure out what T is without needing to explicitly specify it in the caller?
You can extract T from the function type using a traits class.
template<class F>
struct CallbackTraits;
template<class T>
struct CallbackTraits<void(*)(T)>
{
typedef T ArgumentType;
};
Your example can be modified like this:
template<typename F>
void Foo(F func)
{
typedef typename CallbackTraits<F>::ArgumentType T;
}
void Callback(int* data) { }
int Test()
{
Foo(Callback);
}
This technique is used in the boost type-traits library:
http://www.boost.org/doc/libs/1_57_0/libs/type_traits/doc/html/boost_typetraits/reference/function_traits.html
This blog post goes into a bit more detail about the implementation of the technique:
https://functionalcpp.wordpress.com/2013/08/05/function-traits/
Unfortunately this approach hides the information in the signature of Foo about the constraints on the argument passed in. In the above example the argument must be a function of type void(T*).
This alternative syntax does the same as the original example while being slightly more readable:
template<typename T>
void Foo(void func(T*)) { }
Another alternative syntax that may be more readable can be achieved using c++11's alias templates as follows:
template<typename T>
using Identity = T;
template<typename T>
void Foo(Identity<void(T*)> func) { }
Unforunately the latest MSVC fails to compile this, reporting an internal compiler error.
You won't be able to deduce the type based on a nested name: there is no reason why different instantiations of the outer type won't define an identical inner type. You could use a using alias, though:
template <typename T>
using Function = auto (*)(T*) -> void;
template <typename T>
void Foo(Function<T>) {
}
Personally, I would recommend against using any of that, however: in practice it seems much more advisable to actually take a function object which later allows using object with suitable function call operators to be used. For callbacks it is quite common that you'll need to pass in some auxiliary data. That is, you would either use an unconstrained template or one which takes a type-erased type, depending on what you want to do exactly:
template <typename Fun>
void Unconstrained(Fun fun) {
}
template <typename T>
void TypeErased(std::function<void(T*)> fun) {
}
The unconstrained version has the advantage that it can potentially inline the function call but it has the disadvantage that every function object type creates a new instantiation and that the argument types are likely to vary. The type-erased version effectively has to do something like a virtual function call but there is just one instantiation of the function template (per argument type T, of course).
Admittedly, the type-erased version's type won't be deduced from a function pointer (or any other argument which isn't a std::function<void(X*)>), i.e., you may want to have a forwarding function
template <typename T>
void TypeErased(Function<T> fun) {
TypeErased(std::function<void(T)>(fun));
}
In C++98 and C++03 template argument deduction only works with functions (and methods).
I don't think the picture changed in the more recent standards.
Related
I have a function that needs to take shared ownership of an argument, but does not modify it.
I have made the argument a shared_ptr<const T> to clearly convey this intent.
template <typename T>
void func(std::shared_ptr<const T> ptr){}
I would like to call this function with a shared_ptr to a non-const T. For example:
auto nonConstInt = std::make_shared<int>();
func(nonConstInt);
However this generates a compile error on VC 2017:
error C2672: 'func': no matching overloaded function found
error C2784: 'void func(std::shared_ptr<const _Ty>)': could not deduce template argument for 'std::shared_ptr<const _Ty>' from 'std::shared_ptr<int>'
note: see declaration of 'func'
Is there a way to make this work without:
Modifying the calls to func. This is part of a larger code refactoring, and I would prefer not to have to use std::const_pointer_cast at every call site.
Defining multiple overloads of func as that seems redundant.
We are currently compiling against the C++14 standard, with plans to move to c++17 soon, if that helps.
template <typename T>
void cfunc(std::shared_ptr<const T> ptr){
// implementation
}
template <typename T>
void func(std::shared_ptr<T> ptr){ return cfunc<T>(std::move(ptr)); }
template <typename T>
void func(std::shared_ptr<const T> ptr){ return cfunc<T>(std::move(ptr)); }
this matches how cbegin works, and the "overloads" are trivial forwarders with nearly zero cost.
Unfortunately, there is no good solution to what you desire. The error occurs because it fails to deduce template argument T. During argument deduction it attempts only a few simple conversations and you cannot influence it in any way.
Think of it: to cast from std::shared_ptr<T> to some std::shared_ptr<const U> it requires to know U, so how should compiler be able to tell that U=T and not some other type? You can always cast to std::shared_ptr<const void>, so why not U=void? So such searches aren't performed at all as in general it is not solvable. Perhaps, hypothetically one could propose a feature where certain user-explicitly-declared casts are attempted for argument deduction but it isn't a part of C++.
Only advise is to write function declaration without const:
template <typename T>
void func(std::shared_ptr<T> ptr){}
You could try to show your intent by making the function into a redirection like:
template <typename T>
void func(std::shared_ptr<T> ptr)
{
func_impl<T>(std::move(ptr));
}
Where func_impl is the implementation function that accepts a std::shared_ptr<const T>. Or even perform const cast directly upon calling func_impl.
Thanks for the replies.
I ended up solving this a slightly different way. I changed the function parameter to just a shared_ptr to any T so that it would allow const types, then I used std::enable_if to restrict the template to types that I care about. (In my case vector<T> and const vector<T>)
The call sites don't need to be modified. The function will compile when called with both shared_ptr<const T> and shared_ptr<T> without needing separate overloads.
Here's a complete example that compiles on VC, GCC, and clang:
#include <iostream>
#include <memory>
#include <vector>
template<typename T>
struct is_vector : public std::false_type{};
template<typename T>
struct is_vector<std::vector<T>> : public std::true_type{};
template<typename T>
struct is_vector<const std::vector<T>> : public std::true_type{};
template <typename ArrayType,
typename std::enable_if_t<is_vector<ArrayType>::value>* = nullptr>
void func( std::shared_ptr<ArrayType> ptr) {
}
int main()
{
std::shared_ptr< const std::vector<int> > constPtr;
std::shared_ptr< std::vector<int> > nonConstPtr;
func(constPtr);
func(nonConstPtr);
}
The only downside is that the non-const instantiation of func will allow non-const methods to be called on the passed-in ptr. In my case a compile error will still be generated since there are some calls to the const version of func and both versions come from the same template.
As the const is only for documentation, make it a comment:
template <class T>
void func(std::shared_ptr</*const*/ T> p) {
}
You could additionally delegate to the version getting a pointer to constant object if the function is hefty enough to make it worthwhile:
template <class T>
void func(std::shared_ptr</*const*/ T> p) {
if (!std::is_const<T>::value) // TODO: constexpr
return func<const T>(std::move(p));
}
No guarantee the compiler will eliminate the move though.
You certainly don't want to be modifying the call sites, but you sure can be modifying the functions themselves - that's what you implied in the question anyway. Something had to be changed somewhere, after all.
Thus:
In C++17 you could use deduction guides and modify call sites, but in a less intrusive manner than with a cast. I'm sure a language lawyer can pitch in about whether adding a deduction guide to the std namespace is allowed. At the very least we can limit the applicability of those deduction guides to the types we care about - it won't work for the others. That's to limit potential for mayhem.
template <typename T>
class allow_const_shared_ptr_cast : public std::integral_constant<bool, false> {};
template <typename T>
static constexpr bool allow_const_shared_ptr_cast_v = allow_const_shared_ptr_cast<T>::value;
template<>
class allow_const_shared_ptr_cast<int> : public std::integral_constant<bool, true> {};
template <typename T>
void func(std::shared_ptr<const T> ptr) {}
namespace std {
template<class Y> shared_ptr(const shared_ptr<Y>&) noexcept
-> shared_ptr<std::enable_if_t<allow_const_shared_ptr_cast_v<Y>, const Y>>;
template<class Y> shared_ptr(shared_ptr<Y>&&) noexcept
-> shared_ptr<std::enable_if_t<allow_const_shared_ptr_cast_v<Y>, const Y>>;
}
void test() {
std::shared_ptr<int> nonConstInt;
func(std::shared_ptr(nonConstInt));
func(std::shared_ptr(std::make_shared<int>()));
}
std::shared_ptr is certainly less wordy than std::const_pointer_cast<SomeType>.
This should not have any performance impact, but sure modifying the call sites is a pain.
Otherwise there's no solution that doesn't involve modifying the called function declarations - but the modification should be acceptable, I think, since it's not any more wordy than what you had already:
Suppose we want to write a function which is supposed to get a value as a template parameter (for, say, efficiency reasons), but we don't know in advance the type of the parameter we're expecting. It is possible to implement it as
template<typename T, T val>
func() { cout << val; }
However, it is not fun to call such a function
func<int, 5>()
is it possible to rewrite func s.t. we can call it in the following way?
func<5>()
A solution that mostly depends on your actual function is to define it as it follows:
template<typename T>
constexpr void func(T val) { }
Then invoke it as f(5) and have the template parameter deduced from the parameter of the function itself.
Otherwise, in C++14, you cannot avoid using the pattern template<typename T, T value>.
It is the same pattern used by the Standard Template Library, see as an example the definition of std::integral_constant.
A possible solution that mitigates (maybe) the boilerplate is based on the use of a struct, as an example:
template<typename T>
struct S {
template<T value>
static void func() {}
};
You can the do something like this:
using IntS = S<int>;
// ....
IntS::func<5>();
With the upcoming revision C++17, you will manage to do it as it follows:
template<auto value>
void func() {}
This can be invoked as f<5>(), that is what you are looking for..
Is there a way to deduce a template argument for a function pointer when using a typedef? My sample code is:
struct A {};
void func(const A&) {};
template <typename T>
struct FuncPtr
{
typedef void(*Type)(const T&);
};
void f_(FuncPtr<A>::Type) {}
template <typename T> // I'd like to hide the messy function pointer in a typedef
void f1(typename FuncPtr<T>::Type fp) { f_(fp); }
template <typename T> // this works, but "fp" is really several lines long
void f2(void(*fp)(const T&)) { f_(fp); }
With that in place, I can call f2(AFunc). But I'd rather have something closer to f1(func) because in my actual code the declaration for the function pointer is much longer. And I need to have an actual function pointer, rather than just passing a template argument, so that I can call f_().
Not with your current setup. Everything to the left of ::* is a non-deduced context, because a metafunction can apply arbitrary Turing-complete transformations on the type, and in any event there's no guaranteed one-to-one mapping between the original type and the result. So the language doesn't even try.
But an alias template will work:
template <class T>
using FuncPtr = void (*)(const T&);
template <typename T>
void f3(FuncPtr<T> fp) { return f_(fp); }
*Formally known as the "nested-name-specifier of a type that was specified using a qualified-id" ([temp.deduct.type]/p5).
As “Effective Modern C++” Item 9,Prefer alias declarations to typedefs says:
In particular, alias declarations may be templatized (in which case
they’re called alias templates), while typedefs cannot. This gives
C++11 programmers a straightforward mechanism for expressing things
that in C++98 had to be hacked together with typedefs nested inside
templatized structs.For example, consider defining a synonym for a linked list that uses a custom allocator, MyAlloc. With an alias template, it’s a piece of cake:
template<typename T
using MyAllocList = std::list<T, MyAlloc<T>>;
MyAllocList<Widget> lw;
With a typedef, you pretty much have to create the cake from
scratch:
template<typename T>
struct MyAllocList {
typedef std::list<T, MyAlloc<T>> type;
};
MyAllocList<Widget>::type lw;
I am trying to write a code that calls a class method given as template parameter. To simplify, you can suppose the method has a single parameter (of an arbitrary type) and returns void. The goal is to avoid boilerplate in the calling site by not typing the parameter type. Here is a code sample:
template <class Method> class WrapMethod {
public:
template <class Object>
Param* getParam() { return ¶m_; }
Run(Object* obj) { (object->*method_)(param_); }
private:
typedef typename boost::mpl::at_c<boost::function_types::parameter_types<Method>, 1>::type Param;
Method method_;
Param param_
};
Now, in the calling site, I can use the method without ever writing the type of the parameter.
Foo foo;
WrapMethod<BOOST_TYPEOF(&Foo::Bar)> foo_bar;
foo_bar.GetParam()->FillWithSomething();
foo_bar.Run(foo);
So, this code works, and is almost what I want. The only problem is that I want to get rid of the BOOST_TYPEOF macro call in the calling site. I would like to be able to write something like WrapMethod<Foo::Bar> foo_bar instead of WrapMethod<BOOST_TYPEOF(&Foo::Bar)> foo_bar.
I suspect this is not possible, since there is no way of referring to a method signature other than using the method signature itself (which is a variable for WrapMethod, and something pretty large to type at the calling site) or getting the method pointer and then doing typeof.
Any hints on how to fix these or different approaches on how to avoid typing the parameter type in the calling site are appreciated.
Just to clarify my needs: the solution must not have the typename Param in the calling site. Also, it cannot call FillWithSomething from inside WrapMethod (or similar). Because that method name can change from Param type to Param type, it needs to live in the calling site. The solution I gave satisfies both these constraints, but needs the ugly BOOST_TYPEOF in the calling site (using it inside WrapMethod or other indirection would be fine since that is code my api users won't see as long as it is correct).
Response:
As far as I can say, there is no possible solution. This boil down to the fact that is impossible to write something like WrapMethod<&Foo::Bar>, if the signature of Bar is not known in advance, even though only the cardinality is necessary. More generally, you can't have template parameters that take values (not types) if the type is not fixed. For example, it is impossible to write something like typeof_literal<0>::type which evalutes to int and typeof_literal<&Foo::Bar>::type, which would evaluate to void (Foo*::)(Param) in my example. Notice that neither BOOST_TYPEOF or decltype would help because they need to live in the caling site and can't be buried deeper in the code. The legitimate but invalid syntax below would solve the problem:
template <template<class T> T value> struct typeof_literal {
typedef decltype(T) type;
};
In C++0x, as pointed in the selected response (and in others using BOOST_AUTO), one can use the auto keyword to achieve the same goal in a different way:
template <class T> WrapMethod<T> GetWrapMethod(T) { return WrapMethod<T>(); }
auto foo_bar = GetWrapMethod(&Foo::Bar);
Write it as:
template <typename Object, typename Param, void (Object::*F)(Param)>
class WrapMethod {
public:
Param* getParam() { return ¶m_; }
void Run(Object* obj) { (obj->*F)(param_); }
private:
Param param_;
};
and
Foo foo;
WrapMethod<Foo, Param, &Foo::Bar> foo_bar;
foo_bar.getParam()->FillWithSomething();
foo_bar.Run(foo);
EDIT: Showing a template function allowing to do the same thing without any special template wrappers:
template <typename Foo, typename Param>
void call(Foo& obj, void (Foo::*f)(Param))
{
Param param;
param.FillWithSomthing();
obj.*f(param);
}
and use it as:
Foo foo;
call(foo, &Foo::Bar);
2nd EDIT: Modifying the template function to take the initialization function as a parameter as well:
template <typename Foo, typename Param>
void call(Foo& obj, void (Foo::*f)(Param), void (Param::*init)())
{
Param param;
param.*init();
obj.*f(param);
}
and use it as:
Foo foo;
call(foo, &Foo::Bar, &Param::FillWithSomething);
If your compiler supports decltype, use decltype:
WrapMethod<decltype(&Foo::Bar)> foo_bar;
EDIT: or, if you really want to save typing and have a C++0x compliant compiler:
template <class T> WrapMethod<T> GetWrapMethod(T) { return WrapMethod<T>(); }
auto foo_bar= GetWrapMethod(&Foo::Bar);
EDIT2: Although, really, if you want it to look pretty you either have to expose users to the intricacies of the C++ language or wrap it yourself in a preprocessor macro:
#define WrapMethodBlah(func) WrapMethod<decltype(func)>
Have you considered using method templates?
template <typename T> void method(T & param)
{
//body
}
Now the compiler is able to implicitly determine parameter type
int i;
bool b;
method(i);
method(b);
Or you can provide type explicitly
method<int>(i);
You can provide specializations for different data types
template <> void method<int>(int param)
{
//body
}
When you are already allowing BOOST_TYEPOF(), consider using BOOST_AUTO() with an object generator function to allow type deduction:
template<class Method> WrapMethod<Method> makeWrapMethod(Method mfp) {
return WrapMethod<Method>(mfp);
}
BOOST_AUTO(foo_bar, makeWrapMethod(&Foo::Bar));
Okay let's have a go at this.
First of all, note that template parameter deduction is available (as noted in a couple of answers) with functions.
So, here is an implementation (sort of):
// WARNING: no virtual destructor, memory leaks, etc...
struct Foo
{
void func(int e) { std::cout << e << std::endl; }
};
template <class Object>
struct Wrapper
{
virtual void Run(Object& o) = 0;
};
template <class Object, class Param>
struct Wrap: Wrapper<Object>
{
typedef void (Object::*member_function)(Param);
Wrap(member_function func, Param param): mFunction(func), mParam(param) {}
member_function mFunction;
Param mParam;
virtual void Run(Object& o) { (o.*mFunction)(mParam); }
};
template <class Object, class Param>
Wrap<Object,Param>* makeWrapper(void (Object::*func)(Param), Param p = Param())
{
return new Wrap<Object,Param>(func, p);
}
int main(int argc, char* argv[])
{
Foo foo;
Wrap<Foo,int>* fooW = makeWrapper(&Foo::func);
fooW->mParam = 1;
fooW->Run(foo);
Wrapper<Foo>* fooW2 = makeWrapper(&Foo::func, 1);
fooW2->Run(foo);
return 0;
}
I think that using a base class is the native C++ way of hiding information by type erasure.
I currently have a function template, taking a reference, that does something in essence equivalent to:
template <typename T>
void f(T& t)
{
t = T();
}
Now, I can call:
int a;
f(a);
To initialize my variable a.
I can even do:
std::vector<int> a(10);
f(a[5]);
However, this will fail:
std::vector<bool> a(10);
f(a[5]);
The reason being a[5] returns an object with reference semantic, but not a reference. So I need to be able to write:
template <typename T>
void f(T a)
{
a = T();
}
But if I add this new template and try to compile the first example (with int), I obtain the following error:
test_multi_tmpl.cc: In function ‘int main()’:
test_multi_tmpl.cc:20: error: call of overloaded ‘f(int&)’ is ambiguous
test_multi_tmpl.cc:6: note: candidates are: void f(T&) [with T = int]
test_multi_tmpl.cc:12: note: void f(T) [with T = int]
Any ideas how to solve this? I wouldn't like to overload f just for std::vector<bool>::reference as this construct might appears in other places ...
I think specialising f for std::vector<bool>::reference is your only option.
Note that using std::vector<bool> is probably a bad idea in the first place (the std::vector<bool> specialisation is deprecated for future versions of the c++ language) so you could just use std::deque<bool> instead.
I'm not sure whether you already know about this...
The specialization std::vector is not really a STL container because it does not meet the necessary requirements. In particular, it's not possible to created proxied containers that satisfy the STL concepts because of the reference semantics (you can't fake a reference). Check this article for more information. (Also, as Autopulated mentioned there should be a compiler directive to provide control over std::vector in the future C++ Standard.)
A simple workaround that could solve your problem is by overloading function f for this particular type (and others if they appear and are not many). Notice that this is an overload and not an specialization. You might also wanna check this for why not specialize function templates.
void f(std::vector<bool>::reference t)
{
/* ... */
}
There are two ways to do this, one is, as you suggest, specialize for std::vector<bool>::reference. The other is by using type-traits dispatching.
template <class T>
void f (T& t) {
f_impl(t, is_reference_wrapper_type<T>());
}
template <class T>
void f_impl(T& t, mpi::false_) {
t = T();
}
template <class T>
void f_impl(T& t, mpi::true_) {
// do nothing, or voodoo here
}
Note the code above is un-tested and there would be more complex means of dispatching based on a trait -- or a set of traits -- in this situation.
This would also mean that you would need to implement is_reference_wrapper_type like so:
template <class T>
struct is_reference_wrapper_type : mpi::false_ {};
template <>
struct is_reference_wrapper_type<std::vector<bool>::reference> : mpi::true_ {};
Using traits or template specialization would make it work.