Passing template function pointer to template as template parameter is too verbose - c++

template<class T, class... TA> Uptr<T, TA> makeUniquePtr(/*...*/) { /*...*/ };
template<class T, class... TA> Sptr<T, TA> makeSharedPtr(/*...*/) { /*...*/ };
template<typename TFPType, TFPType TFP> void func(/*...*/) { /*...*/ }
template<class T, class... TA> void implementation1(/*...*/)
{
// Can this be reduced to func<&makeUniquePtr, T, TA...>?
func<decltype(&makeUniquePtr<T, TA...>), &makeUniquePtr<T, TA...>>(/*...*/);
}
template<class T, class... TA> void implementation2(/*...*/)
{
// Can this be reduced to func<&makeSharedPtr, T, TA...>?
func<decltype(&makeSharedPtr<T, TA...>), &makeSharedPtr<T, TA...>>(/*...*/);
}
Calling func</*...*/>(/*...*/) is extremely verbose. Is there a way to simply call
func<&makeSharedPtr, T, TA...>(/*...*/)
and internally use
&makeSharedPtr<T, TA...>
without having the user specify it twice?

Create a shared maker stateless function object class, like std::less, but with no template parameters itself: instead operator() perfect forwards to your template function.
Pass it instead of your function pointer.
If you need template arguments passed to it, either create a static method to it, or make the entire template class take the arguments and use them in the operator().
Here is an example of late-bound template arguments (live example):
#include <iostream>
// test stubs:
template<typename... Ts>
using UPtr = int;
template<typename... Ts>
using SPtr = double;
template<typename T, typename...TA>
UPtr<T,TA...> makeUniquePtr() {
std::cout << "makeUniquePtr\n";
return {};
}
template<typename T, typename...TA>
SPtr<T,TA...> makeSharedPtr() {
std::cout << "makeSharedPtr\n";
return {};
}
// perfect forwarding make static functions passed by class name:
struct unique_ptr_maker {
template<typename T, typename...TA, typename...Args>
static UPtr<T, TA...> make(Args&&...args) {
return makeUniquePtr<T, TA...>( std::forward<Args>(args)... );
}
};
struct shared_ptr_maker {
template<typename T, typename...TA, typename...Args>
static SPtr<T, TA...> make(Args&&...args) {
return makeSharedPtr<T, TA...>( std::forward<Args>(args)... );
}
};
// your `func`. It can take args or whatever:
template<typename maker, class T, class... TA> void func() {
std::cout << "func\n";
maker::template make<T, TA...>();
}
// a sample of implementation 1 and 2:
template<class T, class... TA> void implementation1()
{
func<unique_ptr_maker, T, TA...>();
}
template<class T, class... TA> void implementation2()
{
func<shared_ptr_maker, T, TA...>();
}
// and, to test, always instantiate:
int main() {
implementation1<int, double>();
implementation2<int, char>();
return 0;
}
with much of the functionality stubbed out, as you did not detail what it was supposed to do in your question.

Making a few assumptions on intended use from your names, I would try something like this:
template<class T>
struct makeUniquePtr {
template<class... TA>
Uptr<T, TA> operator()(TA&&...) const { /*...*/ }
};
template<class T>
struct makeSharedPtr {
template<class... TA>
Sptr<T, TA> operator()(TA&&...) const { /*...*/ }
};
template<class F> void func(/*...*/) { /*...*/ }
template<class T, class... TA> void implementation1(/*...*/)
{
func<makeUniquePtr<T>>(/*...*/);
}
template<class T, class... TA> void implementation2(/*...*/)
{
func<makeSharedPtr<T>>(/*...*/);
}
Unlike Yakk's solution, here parameters TA... are automatically deduced, as is the case for std::make_unique etc.
EDIT My reference to Yakk's solution was made before that solution was edited. I now see it is expanded.

Related

Is it possible to call a function with all arguments default constructed?

Is it possible to have a function like std::invoke, but this function calls all arguments of the given function automatically with the default constructed types?
#include <iostream>
#include <functional>
// e.g. for a single arg
struct Test{
void operator()(int i) {
std::cout << std::to_string(i) << "\n";
}
};
int main(){
Test test;
std::invoke(test, {}); // this doesn't work, would like it to call with default constructed int (0).
return 0;
}
I would like something like
int main()
{
Test test;
invoke_with_defaults(test); // prints 0
return 0;
}
You need a class with a templated conversion operator, returning {} for any type:
struct DefaultConstruct
{
DefaultConstruct() = default;
DefaultConstruct(const DefaultConstruct &) = delete;
DefaultConstruct &operator=(const DefaultConstruct &) = delete;
template <typename T> operator T() && {return {};}
};
int main()
{
Test test;
std::invoke(test, DefaultConstruct{});
}
It's then possible to write a template that automatically determines how many of those have to be passed:
template <typename F, typename ...P>
decltype(auto) InvokeDefault(F &&func)
{
if constexpr (std::is_invocable_v<F, P...>)
return std::invoke(std::forward<F>(func), P{}...);
else
return InvokeDefault<F, P..., DefaultConstruct>(std::forward<F>(func));
}
int main()
{
Test test;
InvokeDefault(test);
}
And if the argument isn't callable at all, you get a compilation error after exceeding some implementation-defined limit (on Clang I got up to 256).
Initializer lists like {} cannot be forwarded as a parameter due not work due to language restrictions.
But you can mimick {} by wrapping it into a Defaulter class which can be passed around:
#include <iostream>
#include <functional>
// e.g. for a single arg
struct Test{
void operator()(int i) {
std::cout << std::to_string(i) << "\n";
}
};
struct Defaulter{
template<typename T>
operator T(){
return {};
}
};
int main(){
Test test;
std::invoke(test, Defaulter{});
return 0;
}
You could use something like this to create a tuple of all of the argument types, and then pass a default constructed instance of it to std::apply. The specialisation list would need to be quite long though to cover all of the const, volatile, noexcept, and ref-qualified variants though, and of course it cannot work with template or overloaded functions.
Eg:
template <typename T>
struct arg_extractor : arg_extractor<decltype(&T::operator())> {
};
template <typename R, typename... Args>
struct arg_extractor<R (*)(Args...)> {
using type = std::tuple<R, Args...>;
};
template <typename R, typename C, typename... Args>
struct arg_extractor<R (C::*)(Args...)> {
using type = std::tuple<R, Args...>;
};
template <typename R, typename C, typename... Args>
struct arg_extractor<R (C::*)(Args...) const> {
using type = std::tuple<R, Args...>;
};
template <typename R, typename C, typename... Args>
struct arg_extractor<R (C::*)(Args...) noexcept> {
using type = std::tuple<R, Args...>;
};
template <typename R, typename C, typename... Args>
struct arg_extractor<R (C::*)(Args...) const noexcept> {
using type = std::tuple<R, Args...>;
};
// All the rest...
template <typename T>
using arg_extractor_t = typename arg_extractor<T>::type;

How can I have a function pointer template as a template parameter?

I am trying to create a class template that expects a type and a function pointer as template parameters. The function pointer is expected to be a member function of the type passed in. I want the user of the class template to be able to pass in a void member function of the type passed in. That member function will then be called on instances of the type passed in every time a certain function of the class template is called. It's a bit hard to explain but it's supposed to work sort of like this:
template<Type, Function> // For the purpose of explaining it
class Foo
{
public:
template<Args... args>
void callOnAll(Args... args)
{
for(Type* ptr : ptrs_)
{
ptr->Function(std::forward<Args>(args)...);
}
}
private:
std::vector<Type*> ptrs_;
}
Assuming that something like this is possible (which I realize it might not be), the key would have to be getting the template parameters for the class right, and getting the update function right. This is what I've come up with but I still can't get it to work:
template<typename T, template<typename... Args> void(T::*func)(Args... args)>
class EngineSystem
{
public:
template<typename... Args>
void update(Args... args)
{
for (T* handler : handlers_)
{
((*handler).*func)(std::forward<Args>(args)...);
}
}
private:
std::vector<T*> handlers_;
};
The code above does not compile. It points me to the line where I declare the template parameters for the class, underlines void and says expected 'class' or 'typename'.
Is it clear what I'm trying to achieve, and is it possible?
C++ doesn't allow non-type template template parameters. That means you can't have a parameter-pack for your member-function pointer parameter.
Assuming you're using C++17 or newer, you can use an auto template parameter instead:
template<typename T, auto func>
public:
template<typename... Args>
void update(Args... args)
{
for (T* handler : handlers_)
{
(handler->*func)(std::forward<Args>(args)...);
}
}
private:
std::vector<T*> handlers_;
};
Live Demo
Technically that will accept any object for func, but assuming update is called, then (handler->*func)(std::forward<Args>(args)...) still has to be well-formed or compilation will fail.
If you want compilation to fail even if update never gets called, you could use some type traits and a static_assert (or some SFINAE hackery, if you need it) to ensure that func is actually a pointer to a member function of T:
template <typename T, typename U>
struct IsPointerToMemberOf : std::false_type {};
template <typename T, typename U>
struct IsPointerToMemberOf<T, U T::*> : std::true_type {};
template <typename T, typename U>
struct IsPointerToMemberFunctionOf
: std::integral_constant<
bool,
IsPointerToMemberOf<T, U>::value && std::is_member_function_pointer<U>::value
>
{};
template<typename T, auto func>
class EngineSystem
{
static_assert(IsPointerToMemberFunctionOf<T, decltype(func)>::value, "func must be a pointer to a member function of T");
//...
};
Live Demo
#include <iostream>
#include <vector>
template <typename T, typename... Args>
class EngineSystem
{
public:
EngineSystem(void(T::*fun)(Args... args)): fun(fun)
{
}
void update(Args... args)
{
for (T* handler : handlers_)
{
(handler->*fun)(std::forward<Args>(args)...);
}
}
void push(T* t){
handlers_.push_back(t);
}
private:
void(T::*fun)(Args... args);
std::vector<T*> handlers_;
};
struct A {
int x = 3;
void fn(int a, int b){
std::cout << a << b << x;
}
};
template <typename T, typename... Args>
auto makeEngine(void(T::*fun)(Args... args)){
return EngineSystem<T, Args...>(fun);
}
int main() {
EngineSystem<A, int, int> as(&A::fn);
// or deduce types automatically
auto es = makeEngine(&A::fn);
A a;
es.push(&a);
es.update(1,2);
return 0;
}
https://gcc.godbolt.org/z/Pcdf9K9nz

Friend template function instantiations that match parameter

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().

C++ template function with std::optional [duplicate]

I'm trying to write syntactic sugar, in a monad-style, over std::optional. Please consider:
template<class T>
void f(std::optional<T>)
{}
As is, this function cannot be called with a non-optional T1 (e.g. an int), even though there exists a conversion from T to std::optional<T>2.
Is there a way to make f accept an std::optional<T> or a T (converted to an optional at the caller site), without defining an overload3?
1) f(0): error: no matching function for call to 'f(int)' and note: template argument deduction/substitution failed, (demo).
2) Because template argument deduction doesn't consider conversions.
3) Overloading is an acceptable solution for a unary function, but starts to be an annoyance when you have binary functions like operator+(optional, optional), and is a pain for ternary, 4-ary, etc. functions.
Another version. This one doesn't involve anything:
template <typename T>
void f(T&& t) {
std::optional opt = std::forward<T>(t);
}
Class template argument deduction already does the right thing here. If t is an optional, the copy deduction candidate will be preferred and we get the same type back. Otherwise, we wrap it.
Instead of taking optional as argument take deductible template parameter:
template<class T>
struct is_optional : std::false_type{};
template<class T>
struct is_optional<std::optional<T>> : std::true_type{};
template<class T, class = std::enable_if_t<is_optional<std::decay_t<T>>::value>>
constexpr decltype(auto) to_optional(T &&val){
return std::forward<T>(val);
}
template<class T, class = std::enable_if_t<!is_optional<std::decay_t<T>>::value>>
constexpr std::optional<std::decay_t<T>> to_optional(T &&val){
return { std::forward<T>(val) };
}
template<class T>
void f(T &&t){
auto opt = to_optional(std::forward<T>(t));
}
int main() {
f(1);
f(std::optional<int>(1));
}
Live example
This uses one of my favorite type traits, which can check any all-type template against a type to see if it's the template for it.
#include <iostream>
#include <type_traits>
#include <optional>
template<template<class...> class tmpl, typename T>
struct x_is_template_for : public std::false_type {};
template<template<class...> class tmpl, class... Args>
struct x_is_template_for<tmpl, tmpl<Args...>> : public std::true_type {};
template<template<class...> class tmpl, typename... Ts>
using is_template_for = std::conjunction<x_is_template_for<tmpl, std::decay_t<Ts>>...>;
template<template<class...> class tmpl, typename... Ts>
constexpr bool is_template_for_v = is_template_for<tmpl, Ts...>::value;
template <typename T>
void f(T && t) {
auto optional_t = [&]{
if constexpr (is_template_for_v<std::optional, T>) {
return t;
} else {
return std::optional<std::remove_reference_t<T>>(std::forward<T>(t));
}
}();
(void)optional_t;
}
int main() {
int i = 5;
std::optional<int> oi{5};
f(i);
f(oi);
}
https://godbolt.org/z/HXgoEE
Another version. This one doesn't involve writing traits:
template <typename T>
struct make_optional_t {
template <typename U>
auto operator()(U&& u) const {
return std::optional<T>(std::forward<U>(u));
}
};
template <typename T>
struct make_optional_t<std::optional<T>> {
template <typename U>
auto operator()(U&& u) const {
return std::forward<U>(u);
}
};
template <typename T>
inline make_optional_t<std::decay_t<T>> make_optional;
template <typename T>
void f(T&& t){
auto opt = make_optional<T>(std::forward<T>(t));
}

Function template taking a template non-type template parameter

How does one take a templated pointer to a member function?
By templated I mean that the following types are not known in advance:
template param T is class of the pointer to member
template param R is the return type
variadic template param Args... are the parameters
Non-working code to illustrate the issue:
template <???>
void pmf_tparam() {}
// this works, but it's a function parameter, not a template parameter
template <class T, typename R, typename... Args>
void pmf_param(R (T::*pmf)(Args...)) {}
struct A {
void f(int) {}
};
int main() {
pmf_tparam<&A::f>(); // What I'm looking for
pmf_param(&A::f); // This works but that's not what I'm looking for
return 0;
}
Is it possible to achieve the desired behavior in C++11?
I don't think this notation is possible, yet. There is proposal P0127R1 to make this notation possible. The template would be declared something like this:
template <auto P> void pmf_tparam();
// ...
pmf_tparam<&S::member>();
pmf_tparam<&f>();
The proposal to add auto for non-type type parameters was voted into the C++ working paper in Oulu and the result was voted to become the CD leading towards C++17 also in Oulu. Without the auto type for the non-type parameter, you'd need to provide the type of the pointer:
template <typename T, T P> void pmf_tparam();
// ...
pmf_tparam<decltype(&S::member), &S::member>();
pmf_tparam<decltype(&f), &f>();
As you've not said really what you are after in the function, the simplest is:
struct A {
void bar() {
}
};
template <typename T>
void foo() {
// Here T is void (A::*)()
}
int main(void) {
foo<decltype(&A::bar)>();
}
However if you want the signature broken down, I'm not sure there is a way to resolve the types directly, however you can with a little indirection...
struct A {
void bar() {
std::cout << "Call A" << std::endl;
}
};
template <typename R, typename C, typename... Args>
struct composer {
using return_type = R;
using class_type = C;
using args_seq = std::tuple<Args...>;
using pf = R (C::*)(Args...);
};
template <typename C, typename C::pf M>
struct foo {
static_assert(std::is_same<C, composer<void, A>>::value, "not fp");
typename C::return_type call(typename C::class_type& inst) {
return (inst.*M)();
}
template <typename... Args>
typename C::return_type call(typename C::class_type& inst, Args&&... args) {
return (inst.*M)(std::forward<Args...>(args...));
}
};
template <class T, typename R, typename... Args>
constexpr auto compute(R (T::*pmf)(Args...)) {
return composer<R, T, Args...>{};
}
int main() {
foo<decltype(compute(&A::bar)), &A::bar> f;
A a;
f.call(a);
}
The above should do what you are after...
What you can do is
template <template T, T value>
void pmf_tparam() {}
and then
pmf_tparam<decltype(&A::f), &A::f>();
The problem is not knowing the type of the argument and wanting a template argument of that type.
With an additional decltype (still in the templated parameter), this works:
#include <iostream>
using namespace std;
template <typename T, T ptr>
void foo (){
ptr();
}
void noop() {
cout << "Hello" << endl;
}
int main() {
//Here have to use decltype first
foo<decltype(&noop), noop>();
return 0;
}