Friend template function instantiations that match parameter - c++

I have a template class that should have a friend: a make_object function that allows to deduct certain types and values. I wish to make friends only those instantiations that match the types of the template class. The simplified version of my code is below:
template<size_t S, typename T>
class Object {
private:
Object(T t);
template<typename U, typename... Args>
friend auto make_object(U, Args... args);
};
template<typename U, typename... Args>
inline auto make_object(U u, Args... args)
{
constexpr size_t S = sizeof...(Args);
return std::unique_ptr<Object<S, U>>(new Object<S, U>(u));
}
Taking this code as an example I wish to make friends only those instantiations of make_object whose typename U matches the typename T of the object. Is that even possible?

If your requirement is simply befriending a function template with the same template parameter as T, then this would be sufficient:
#include <cstdint>
template <typename T, typename ... TArgs>
auto make_object(T, TArgs...);
template<std::size_t size, typename T>
class Object {
private:
Object(T t);
friend auto make_object<T>(T);
};
template <typename T, typename ... TArgs>
auto make_object(T t, TArgs... args) {
Object<0u, T>{t}; // Compiles
}
template <>
auto make_object<float>(float f) {
// Error: Constructor is private
Object<0u, int>{static_cast<int>(f)};
}
Compiler Explorer

You can use a class to separate the U parameter of make_object (which has to match with the T of Object) from the Args parameter pack, which does not have to match. Then, befriend the helper class, giving all instantiations of your function access to the private members of Object.
template<typename U>
struct make_object_t;
template<std::size_t S, typename T>
class Object {
private:
Object(T t);
friend class make_object_t<T>;
};
template<typename U>
struct make_object_t {
template<typename... Args>
static auto make_object(U u, Args... args) {
constexpr std::size_t S = sizeof...(Args);
return std::unique_ptr<Object<S, U>>(new Object<S, U>(u));
}
};
Finally, write a helper function to hide the class from the API:
template<typename U, typename... Args>
auto make_object(U&& u, Args&&... args) {
return make_object_t<U>::template make_object<Args...>(std::forward<U>(u), std::forward<Args>(args)...);
}
Now, e.g. this works
int main() {
std::unique_ptr<Object<3, int>> ptr = make_object(5, 'a', 0x0B, "c");
}
But, e.g. make_object_t<char> cannot use Object<S, int>::Object:
template<>
struct make_object_t<char> {
template<typename... Args>
static auto make_object(char u, Args... args) {
constexpr std::size_t S = sizeof...(Args);
return std::unique_ptr<Object<S, int>>(new Object<S, int>(u));
}
};
int main() {
// error here from instantiation of above function template
auto oops = make_object('n', 'o');
}

If you want to have a friend template, you can make all the instatiations of the template a friend, like in your example, or you can make a full specialization a friend.
There is nothing in between unfortunately.
To work around that you could wrap your make_object function in a template class and make that template class a friend.
#include <type_traits>
#include <iostream>
#include <memory>
template<typename U>
struct object_maker;
template<std::size_t S, typename T>
class Object {
private:
Object(T) {}
friend struct object_maker<T>;
};
template<typename U>
struct object_maker
{
template<typename... Args>
static auto make_object(U u, Args... args)
{
constexpr std::size_t S = sizeof...(Args);
return std::unique_ptr<Object<S, U>>(new Object<S, U>(u));
}
};
int main()
{
auto obj = object_maker<int>::make_object(7);
static_assert(std::is_same_v<decltype(obj), std::unique_ptr<Object<0,int>>>);
}

As far as I understand, make_object() is a convenience function template that takes advantage of template argument deduction for creating Object objects.
You can create an additional function template, e.g., create_object(), that corresponds to the same template parameters as the Object class template: size_t and a type template parameter. Then, you can easily grant friendship in Object to an instance of this function template that has the same template arguments as Object:
template<size_t, typename>
class Object;
template<size_t S, typename T>
inline auto create_object(T t) {
return std::unique_ptr<Object<S, T>>(new Object<S, T>(t));
}
template<size_t S, typename T>
class Object {
private:
Object(T t);
friend auto create_object<S, T>(T);
};
Your original make_object() function template just delegates to this new function template, create_object():
template<typename U, typename... Args>
inline auto make_object(U u, Args... args)
{
constexpr size_t S = sizeof...(Args);
return create_object<S, U>(std::move(u));
}
Once you know the number of elements in the parameter pack Args, you don't need the parameter pack any longer. You only pass the deduced U and the calculated S as template arguments to create_object().

Related

Infer the type of the class owning a member function

Let's consider a kind of "invoke" function (here named "call") that helps to call a member function passed by a template argument.
In this function, I would need to know the type of the class that owns the member function. Is there a way (in c++14 preferably) to do so ?
#include <functional>
template<typename F, typename... Args, std::enable_if_t<std::is_member_pointer<std::decay_t<F>>{}, int> = 0 >
constexpr decltype(auto) call(F&& f, Args&&... args) noexcept(noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
{
// Here we know that f is a member function, so it is of form : &some_class::some_function
// Is there a way here to infer the type some_class from f ? For exemple to instantiate a variable from it :
// imaginary c++ : class_of(f) var;
return std::mem_fn(f)(std::forward<Args>(args)...);
}
int main()
{
struct Foo { void bar() {} } foo;
call(&Foo::bar, &foo /*, args*/);
return 0;
}
template <typename>
struct class_of;
template <typename C, typename R, typename... Args>
struct class_of<R(C::*)(Args...)> {
using type = C;
};
Then you can get the type like typename class_of<F>::type. E.g.
template<typename F, typename... Args, std::enable_if_t<std::is_member_pointer<std::decay_t<F>>{}, int> = 0 >
constexpr decltype(auto) call(F&& f, Args&&... args) noexcept(noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
{
typename class_of<F>::type var;
return std::mem_fn(f)(std::forward<Args>(args)...);
}
BTW, It depends on your intent, but you can change the template paramters' declaration to get the class type directly.
// I remove the type check because only member function pointer could be passed in
template<typename C, typename R, typename... FArgs, typename... Args>
constexpr decltype(auto) call(R(C::*f)(FArgs...), Args&&... args) noexcept(noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
{
C var;
return std::mem_fn(f)(std::forward<Args>(args)...);
}
songyuanyao beat me to it, but note that you don't need to spell out the function type to deconstruct the member pointer:
template <class MemberPtr>
struct mptr_class;
template <class T, class Class>
struct mptr_class<T Class::*> {
using type = Class;
};
template <class MemberPtr>
using mptr_class_t = typename mptr_class<MemberPtr>::type;

How to partially specialize a struct template for non-member & member function pointer

I'm doing some template metaprogramming involving function pointers. Because the function types for non-member and member function pointers are different, I'm trying to specialize between the two. Furthermore, I want the pointer to the function to be provided as a non-type template argument instead of a parameter to a function in the struct. So far here's what I have attempted:
template <typename T, T>
struct register_method;
template <typename R, typename... Args>
struct register_method<R(Args...), R (*method)(Args...)>
{
static R invoke(Args&&... params)
{
return (*method)(std::forward<Args>(params)...);
}
};
template <typename T, typename R, typename... Args>
struct register_method<R(Args...), R (T::*method)(Args...)>
{
static R invoke(T* instance, Args&&... params)
{
return (instance->*method)(std::forward<Args>(params)...);
}
};
However this fails to compile (here's just the first error):
prog.cc:14:48: error: 'Args' does not refer to a value
struct register_method<R(Args...), R (*method)(Args...)>
^
prog.cc:13:35: note: declared here
template <typename R, typename... Args>
^
I'm not really sure what it is trying to tell me. My goal is to utilize these objects like so:
void the_func(float val)
{
std::cout << "the_func called: " << val << "\n";
}
int main()
{
register_method<decltype(&the_func), &the_func>::invoke(50.f);
}
How can I get this working? If there's an opportunity to simplify, that would be great too (for example, if I can get away with just passing the function pointer to the template instead of also doing a decltype, which should reduce boilerplate.
EDIT: Also want to add that the reason I need separate specializations for non-member and member functions extend beyond just the function types being different. There is static state between the two that is different, due to the business rules I attach to the different function types. I have omitted those details here to keep the question simple.
Here's how you can fix the code:
template <typename T, T>
struct register_method;
template <typename R, typename... Args, R (*method)(Args...)>
struct register_method<R (*)(Args...), method>
{
template <typename ...P>
static R invoke(P &&... params)
{
return (*method)(std::forward<P>(params)...);
}
};
template <typename T, typename R, typename... Args, R (T::*method)(Args...)>
struct register_method<R (T::*)(Args...), method>
{
template <typename ...P>
static R invoke(T *instance, P &&... params)
{
return (instance->*method)(std::forward<P>(params)...);
}
};
Note that you have to introduce a separate parameter pack for the forwarding references to work, since they only work if the template parameter is being deduced.
And here is an alternative solution using C++17 auto template parameters:
template <auto method>
struct register_method;
template <typename R, typename... Args, R (*method)(Args...)>
struct register_method<method>
{
template <typename ...P>
static R invoke(P &&... params)
{
return (*method)(std::forward<P>(params)...);
}
};
template <typename T, typename R, typename... Args, R (T::*method)(Args...)>
struct register_method<method>
{
template <typename ...P>
static R invoke(T *instance, P &&... params)
{
return (instance->*method)(std::forward<P>(params)...);
}
};
Specialization should look like
template <typename T, T>
struct register_method;
template <typename R, typename... Args, R (*method)(Args...)>
struct register_method<R (*)(Args...), method>
{
static R invoke(Args... params)
{
return (*method)(std::forward<Args>(params)...);
}
};
template <typename C, typename R, typename... Args, C (T::*method)(Args...)>
struct register_method<R (C::*)(Args...), method>
{
static R invoke(T* instance, Args... params)
{
return (instance->*method)(std::forward<Args>(params)...);
}
};

Hiding template arguments in std::variant

I have a set of template classes that I want to put inside a std::variant. The following works, but is rather ugly (I have many more classes in the variant, with many template parameters).
template <typename T>
struct Type1
{ };
template <typename B, typename C>
struct Type2
{ };
template <typename A, typename B, typename C>
using Variant = std::variant<Type1<A>, Type2<B,C>>;
Is it possible to "hide" the template arguments in a similar way to this? (doesn't compile)
template <typename A>
using Type1T = Type1<A>
template <typename B, typename C>
using Type2T = Type2<B, C>
using Variant = std::variant<Type1T, Type2T>
error: type/value mismatch at argument 1 in template parameter list
for 'template class std::variant'
Adding typename also doesn't seem to work.
You can't use std::variant to do that. You can use std::any, but you can't visit an any.
The problem is that Type1 and Type2 are not types, they are type templates. There is an infinity of instantiations of Type1, which are all unrelated types as far as a visit is concerned.
I suggest you make non-template Type1Base which you derive Type1<T> from, and have a std::variant<std::unique_ptr<Type1Base>, std::unique_ptr<Type2Base>, ...>
Edit - even template_variant is not possible, there is no way to recover which instantiation of the selected template is the active member. If that were possible, you have the complication that every visitor has to have an overload for every instantiation of every template.
Original attempt:
You could write your own template variant, which would have a mix of members similar to std::any and std::variant
template<template<class...> class ... Templates>
class template_variant
{
std::any value;
public:
// constructors, operator=, etc
// Observers
constexpr std::size_t index() const noexcept; // Which template this contains
const std::type_info& type() const noexcept; // returns the typeid of the contained value
// Modifiers
template <template<typename...> class T, class... Args>
T<Args...>&
emplace(Args&&... args)
{
value.emplace<T<Args...>>(std::forward<Args>(args)...);
}
template <size_t I, class... Args>
template_variant_alternative_t<I, variant, Args...>&
emplace(Args&&... args)
{
value.emplace<template_variant_alternative_t<I, variant, Args...>>(std::forward<Args>(args)...);
}
void swap( template_variant& rhs ) noexcept;
// Non-member functions
friend template <std::size_t I, template<class...> class ... Templates, class... Args>
constexpr template_variant_alternative_t<I, template_variant<Templates...>, Args...>&
std::get(template_variant<Templates...>& v)
{
try
{
return std::any_cast<template_variant_alternative_t<I, template_variant<Templates...>, Args...>&>(v.value);
}
catch (std::bad_any_cast & e)
{
throw bad_template_variant_access(e);
}
}
// and &&, and const&, and const&&
template <template<class...> class T, template<class...> class ... Templates, class... Args>
constexpr T<Args...>&
std::get(template_variant<Templates...>& v)
{
try
{
return std::any_cast<T<Args...>&>(v.value);
}
catch (std::bad_any_cast & e)
{
throw bad_template_variant_access(e);
}
}
// and &&, and const&, and const&&
// etc.
};
template <std::size_t I, typename Variant, class... Args>
struct template_variant_alternative;
template <std::size_t I, template<class...> class ... Templates, class... Args>
struct template_variant_alternative<I, template_variant<Templates...>, Args...>
{
using type = // apply Args... to Ith template in Templates
};
template <std::size_t I, typename Variant, class... Args>
using template_variant_alternative_t = template_variant_alternative<I, Variant, Args...>::type;

Transform typelist with function at runtime

I have a typelist. I would like to create a tuple with the results of calling a function on each type in that list and then use that as arguments to another functor. So something like this:
template<typename F>
struct function_traits;
template<typename T, typename R, typename... Args>
struct function_traits<R(T::*)(Args...) const> {
using return_type = R;
using param_types = std::tuple<Args...>;
};
template<typename T> struct function_traits : public
function_traits<decltype(&T::operator())> {};
template <typename T>
T* get_arg(int id)
{
// Actual implementation omitted. Uses the id parameter to
// do a lookup into a table and return an existing instance
// of type T.
return new T();
}
template <typename Func>
void call_func(Func&& func, int id)
{
using param_types = function_traits<Func>::param_types>;
func(*get_arg<param_types>(id)...); // <--- Problem is this line
}
call_func([](int& a, char& b) { }, 3);
The problem is that func(*get_arg<param_types>(id)...); doesn't actually compile since param_types is a tuple and not a parameter pack. The compiler generates this error: "there are no parameter packs available to expand". What I would liked to have happened is for that line to expand to:
func(*get_arg<int>(id), *get_arg<char>(id));
And to have that work for any number of arguments. Is there any way to get that result?
This question seems similar but does not solve my problem by itself: "unpacking" a tuple to call a matching function pointer. I have a type list and from that I want to generate a list of values to use as function arguments. If I had the list of values I could expand them and call the function as outlined in that question, but I do not.
Not sure that is what do you want.
I don't know how to expand, inside call_func(), the parameters pack of params_type but, if you afford the use of a helper struct and a compiler with C++14...
I've prepared the following example with support for return type.
#include <tuple>
template<typename F>
struct function_traits;
template<typename T, typename R, typename... Args>
struct function_traits<R(T::*)(Args...) const> {
using return_type = R;
using param_types = std::tuple<Args...>;
};
template<typename T> struct function_traits : public
function_traits<decltype(&T::operator())> {};
template <typename T, typename ... Args>
T get_arg (std::tuple<Args...> const & tpl)
{ return std::get<typename std::decay<T>::type>(tpl); }
template <typename ...>
struct call_func_helper;
template <typename Func, typename Ret, typename ... Args>
struct call_func_helper<Func, Ret, std::tuple<Args...>>
{
template <typename T, typename R = Ret>
static typename std::enable_if<false == std::is_same<void, R>::value, R>::type
fn (Func const & func, T const & t)
{ return func(get_arg<Args>(t)...); }
template <typename T, typename R = Ret>
static typename std::enable_if<true == std::is_same<void, R>::value, R>::type
fn (Func const & func, T const & t)
{ func(get_arg<Args>(t)...); }
};
template <typename Func,
typename T,
typename R = typename function_traits<Func>::return_type>
R call_func (Func const & func, T const & id)
{
using param_types = typename function_traits<Func>::param_types;
return call_func_helper<Func, R, param_types>::fn(func, id);
}
int main()
{
call_func([](int const & a, char const & b) { }, std::make_tuple(3, '6'));
return 0;
}
Hope this helps.

Initialize class containing a std::function with a lambda

I created a template class containing a std::function as a member the following way:
template<typename Ret, typename... Args>
class Foo
{
private:
std::function<Ret(Args...)> _func;
public:
Foo(const std::function<Ret(Args...)>& func):
_func(func)
{}
};
In order not to have to specify the arguments and return type of the passed function, I created some make_foo overloads:
template<typename Ret, typename... Args>
auto make_foo(Ret (&func)(Args...))
-> Foo<Ret, Args...>
{
return { std::function<Ret(Args...)>(func) };
}
template<typename Ret, typename... Args>
auto make_foo(const std::function<Ret(Args...)>& func)
-> Foo<Ret, Args...>
{
return { func };
}
However, I was unable to create a make_foo overload that takes a lambda as parameter:
template<typename Ret, typename... Args>
auto make_foo(??? func)
-> Foo<Ret, Args...>
{
return { std::function<Ret(Args...)>(func) };
}
I just can't find a way to have the return type and argument types automatically deduced from the lambda. Is there an idiomatic way to solve such a problem?
Ok, so I thought I would die, but I finally managed to do it ç_ç
First, I used the usual indices. Since I do not have the official ones, I used old indices I wrote some months ago:
template<std::size_t...>
struct indices {};
template<std::size_t N, std::size_t... Ind>
struct make_indices:
make_indices<N-1, N-1, Ind...>
{};
template<std::size_t... Ind>
struct make_indices<0, Ind...>:
indices<Ind...>
{};
Then, I used some function traits found somewhere on StackOverflow. They are nice, and I think that they are equivalent to the Boost library linked in the comments:
template<typename T>
struct function_traits:
function_traits<decltype(&T::operator())>
{};
template<typename C, typename Ret, typename... Args>
struct function_traits<Ret(C::*)(Args...) const>
{
enum { arity = sizeof...(Args) };
using result_type = Ret;
template<std::size_t N>
using arg = typename std::tuple_element<N, std::tuple<Args...>>::type;
};
Then, I was able to write a proper make_foo function and it implementation function, since both are required to use indices. Be careful, it's plain ugly:
template<typename Function, std::size_t... Ind>
auto make_foo_(Function&& func, indices<Ind...>)
-> Foo<
typename function_traits<typename std::remove_reference<Function>::type>::result_type,
typename function_traits<typename std::remove_reference<Function>::type>::template arg<Ind>...>
{
using Ret = typename function_traits<typename std::remove_reference<Function>::type>::result_type;
return { std::function<Ret(typename function_traits<typename std::remove_reference<Function>::type>::template arg<Ind>...)>(func) };
}
template<typename Function, typename Indices=make_indices<function_traits<typename std::remove_reference<Function>::type>::arity>>
auto make_foo(Function&& func)
-> decltype(make_foo_(std::forward<Function>(func), Indices()))
{
return make_foo_(std::forward<Function>(func), Indices());
}
The code is somehow ugly and unreadable, but it definitely works. Hope it does not rely on some implementation-defined behaviour now. Also, thanks all for your advice, it helped! :)
int main()
{
auto lambda = [](int i, float b, long c)
{
return long(i*10+b+c);
};
auto foo = make_foo(lambda);
std::cout << foo(5, 5.0, 2) << std::endl; // 57, it works!
}
And here is the live example :)
I have an example that works with mutable lambdas. I can't quite figure out how to get the CV member qualification right.
First, here's the function template we're after:
#include <functional>
template <typename R, typename ...Args>
void foo(std::function<R(Args...)> f)
{ }
Now we'll let a function template bar take an arbitrary lambda and call the right version of foo, by inspecting the type of the lambda's operator():
#include <type_traits>
template <typename> struct remove_member;
template <typename C, typename T>
struct remove_member<T C::*>
{ using type = T; };
template <typename F>
void bar(F f)
{
using ft = decltype(&F::operator());
foo(std::function<typename remove_member<ft>::type>(f));
}
Example:
int q;
bar([&](int a, int b) mutable -> int { q = a + b; return q / b; });
You can use normal, const lambdas with this modified trait, though I don't like having to spell the function type out:
template <typename C, typename R, typename ...Args>
struct remove_member<R (C::*)(Args...) const>
{ using type = R(Args...); };
I thought it might work with the original code if I use typename std::remove_cv<T>::type, but at least on GCC this doesn't work because of some strange __attribute__((const)) that's set on the lambda's operator type which seems to interfere with the template specialization.