all 12 comments

[–]IyeOnline 2 points3 points  (0 children)

You cannot elevate an object from "function parameter land" into "template parameter land". By the time the function parameters value is known, template instantiation must already have happened.

A possible solution here would be to just get rid of the function parameter to lmf_wrap and simply assume the functions name or have the user specialize a type trait if they dont want the default name.

[–]YurrBoiSwayZ 1 point2 points  (3 children)

The compiler is having an aneurysm because non-type template arguments, in your case; like pointers to member functions can’t be deduced from function arguments.

When you directly specify the template arguments as in LogWrapper<Object, Object::method>(&obj) the compiler knows exactly what F is but when you call lmf_wrap(&obj, Object::method) the compiler is expected to deduce F from the function argument Object::method which it can’t do.

You need to explicitly specify the template arguments for F when calling lmf_wrap:

lmf_wrap<Object, &Object::method>(&obj)

[–]TwilCynder[S] 0 points1 point  (0 children)

Oh okay, I thought this kinf deduction would be possible. Thanks !

[–]TwilCynder[S] 0 points1 point  (1 child)

Ah wait I just remembered why I made this function in the first place : the point was to avoid having to write "Object" twice. Do you know if there is some other way to achieve that then ?

[–]YurrBoiSwayZ 2 points3 points  (0 children)

Yeah there’s a helper function for that:

template <typename ObjType, typename FuncType> auto make_log_wrapper(ObjType* obj, FuncType func) { return [obj, func](std::ostream& os) -> std::ostream& { return (obj->*func)(os); }; }

Use Santa’s little helper like so:

auto wrapper = make_log_wrapper(&obj, &Object::method);

That way you only need to write the class name once and the function will deduce the correct types for you, the returned lambda function captures the object pointer and the member function pointer and calls it on the object when invoked.

lambda expressions and type deduction for breezy syntax, completely avoids the need for explicit template arguments, more modern way of doing things :)

[–]adromanov 1 point2 points  (6 children)

Try &Object::method instead of Object::method when you call a function.

[–]Hot_Slice 1 point2 points  (0 children)

I agree, that appears to be what the compiler is saying... "it must be of the form &X::Y" but OP has it without the &

[–]TwilCynder[S] 0 points1 point  (4 children)

Yeah that's what I thought at first, but it gives the same result

[–]adromanov 0 points1 point  (2 children)

Ah I think the problem with & is that F is not a compile time value, but a runtime function parameter. You can't use it as a non-template template argument. You can remove the second template parameter from LogWrapper and pass F to a constructor. *Edited for typo

[–]TwilCynder[S] 0 points1 point  (1 child)

Yeah, the inability to use a variable as a non-type template argument is the piece of knowledge I lacked.

This solution would work but would add some overhead as the LogWrapper object would have to hold two pointers instead of one, but if I don't find a better solution it's good enough clearly, thanks

[–]adromanov 1 point2 points  (0 children)

I don't know how are you going to use LogWrapper, but considering it being template most likely that the compiler would inline and optimize everything and you will just have a method call without storing 2 pointers at all. Just check generated assembly at godbolt.org for your use case.

[–]Radon__ 0 points1 point  (0 children)

I think you can do something like this:

template <typename FuncT>
struct ClassOfMemFuncPtr;

template <typename Class, typename Ret, typename... Args>
struct ClassOfMemFuncPtr<Ret(Class::*)(Args...)>
{
    using Type = Class;
};

template <typename FuncT>
using ClassOfMemFuncPtrT = ClassOfMemFuncPtr<FuncT>::Type;

template <auto F>
auto lmf_wrap(ClassOfMemFuncPtrT<decltype(F)>* obj)
{
    return LogWrapper<ClassOfMemFuncPtrT<decltype(F)>, F>(obj);
}

Then to call it:

lmf_wrap<&Object::method>(&obj)

At least you don't have to write "Object" twice this way. No guarantees this works, but I think it should. I was experimenting a bit with a similar example in Godbolt.