Passing a function as a template parameter

Pseudocode:
1
2
3
4
5
6
7
8
9
10
template<typename T /*, some parameter for member_function */>
class Foo
{
public:
    void someFunction()
    {
        T t;
        t.member_fuction(...);
    }
}


I'm trying to make the call to T::member_function a templated value because member_function might vary by name in my scenario. Since std::mem_fn isn't a 'type', i can't do something like Foo<std::string, std::mem_fn(&std::string::clear)> foo;

I also want to take into account that member_function might have more than one parameter. That is, the first parameter will always be something known but there might be other defaulted parameters.

The only thing I can think of is to make a proxy structure, something like this:
1
2
3
4
5
6
7
8
9
template<typename T, T> struct proxy;
template<typename T, typename R, typename... Args, R (T::*member_function)(Args...)>
struct proxy<R (T::*)(Args...), member_function>
{
    R operator()(T &obj, Args.. args)
    {
        return (obj.*member_function)(std::forward<Args>(args)...);
    }
}


Which would then allow me to do (for example) this:
 
Foo<std::string, proxy<void(std::string::*)(), &std::string::clear> >

when Foo is implemented like this:
1
2
3
4
5
6
7
8
9
10
template<typename T, typename member_function>
class Foo
{
public:
    void someFunction()
    {
        T t;
        member_function()(t);
    }
};


That implementation works for me, but I can't help but feel like there's a better way to go about doing it. Any ideas?
Last edited on
closed account (o1vk4iN6)
You could simply use std::function rather than creating your own.

Do you really need a template here ?

1
2
3
4
5
    void someFunction()
    {
        T t;
        member_function()(t);
    }


regardless of member_function type, you will always be calling a void(), so the template is adding nothing but bloat.
xerzi wrote:
You could simply use std::function rather than creating your own.
Could you elaborate on what you mean?

xerzi wrote:
Do you really need a template here ?
It's templated because T is variable and I don't know which member function I need to call. That's what I want to be able to specify in the template.
closed account (o1vk4iN6)
The std::function is basically what you are creating with your proxy template.

1
2
3
4
5
6
7
std::string str = "full";

std::function<void()> func = std::bind(&std::string::clear, &str);

func();

std::cout << "std: " << str;


It's templated because T is variable and I don't know which member function I need to call. That's what I want to be able to specify in the template.

Well you know the function definition, you just want to template the parameter it takes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template<typename T>
class Foo
{
public:

    Foo(std::function<void(T&)> func) : m_func(func) { }

    void someFunction() { T t; m_func(t); }

private:
    std::function<void(T&)> m_func;

};

auto foo = Foo<std::string>(&std::string::clear);
> It's templated because T is variable and I don't know which member function I need to call.
> That's what I want to be able to specify in the template.

Templates become more flexible when we do not over-specify the template parameters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>
#include <functional>
#include <queue>
#include <string>

struct command
{
    template < typename CALLABLE, typename... ARGS >
    void add( CALLABLE&& fn, ARGS&&... args )
    { tasks.emplace( std::bind( fn, std::forward<ARGS>(args)... ) ) ; }

    void operator() ()
    {
        while( !tasks.empty() )
        {
            tasks.front()() ;
            tasks.pop() ;
        }
    }

    private: std::queue< std::function< void() > > tasks ;
};

int main()
{
    struct A
    {
        int foo() const { std::cout << "A::foo(int) const\n" ; return 0 ; }
        void bar( int, int ) const { std::cout << "A::bar(int,int)\n" ; }
        double baz( int& a, int b, float c, long d )
        { std::cout << "A::baz(int,int,float,long)\n" ; return v += a += b+c+d ; }

        static void foobar( int, double )
        { std::cout << "static void A::foobar( int, double )\n" ; }

        double v = 5.6 ;
    };

    command cmd ;
    A a ;

    cmd.add( &A::foo, a ) ;
    cmd.add( &A::bar, std::ref(a), 12, 345 ) ;

    int i = 12 ;
    cmd.add( &A::baz, std::addressof(a), std::ref(i), 345, 6.74, 8 ) ;

    cmd.add( &A::foobar, 12, 8 ) ;

    cmd.add( []( A, const char*) { std::cout << "closure\n" ; }, a, "hello" ) ;

    std::string str ;
    cmd.add( &std::string::reserve, std::ref(str), 1024*1024 ) ;

    std::cout << "*** execute command ***\n" ;
    cmd() ;
    std::cout << "\ni == " << i << "  a.v == " << a.v
               << "\nstr.capacity() == " << str.capacity() << '\n' ;
}

http://coliru.stacked-crooked.com/a/8f654b52adb50630
Last edited on
Alright, I see what you guys are saying. And thank you JLBorges that was incredibly helpful.
I'm having trouble storing the std::function object. That is, my compiler keeps complaining that I try to construct an object of type std::function<void()> with something else (which I guess makes sense).

It's looking like this:
1
2
3
4
5
6
7
8
struct Foo
{
    Foo(std::function<void()> fn) : load_function(fn) {}
private:
    std::function<void()> load_function;
};

Foo foo(std::mem_fn(&sf::Texture::loadFromFile)); //for example 


Dealing with functions and stuff is still a relatively new area too me. I appreciate all of your guys' help.
Last edited on
The return type of std::mem_fn is unspecified, so you can only ever capture it with auto because you can't know what the return type is in advance.
Yeah that's the problem I've been running into. I would be so nice if it could be specified.
Unfortunately you can't declare class members (that aren't const static) of type 'auto' because they need to be deduced at compile time.
closed account (o1vk4iN6)
Is this for some kind of resource manager ? It will be easier to help you if you don't just abstract the main idea away, there might be an entirely different and better solution.

Anyways you are trying to turn "bool(const std::string&, ... )" into "void()". So that's another problem.

You can express a member function simple as a function like so:

 
std::function<bool(sf::Texture&, const std::string&, const IntRect&)> func = &sf::Texture::loadFromFile;


Yeah xerzi it is.
I wanted to abstract it because I want to understand it conceptually instead of being helped through a solution to my current situation that I won't be able to extrapolate to future events.

I know that that's what the std::function would look like for a texture, but it's different for? Say, sf::Fonts where fonts doesn't have the intrect parameter. Thats what I was trying to abstract.

Anyway, I just resolved to making an sfml_resource_loader structure and using that as a template parameter. So in the future other loading structs could potentially be plugged in.
Resulted in the following: https://github.com/Thumperrr/ChessPlusPlus/blob/0fa96a0af92185468186efadba01e4ee1bddc0f4/src/ResourceManager.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <iostream>
#include <functional>
#include <string>
#include <fstream>

struct foo
{
    template < typename CALLABLE, typename... ARGS >
    explicit foo( CALLABLE&& fn, ARGS&&... args ) :
        load_function( std::bind( fn, std::forward<ARGS>(args)... ) ) {}

    void do_load() { load_function() ; }

    private: std::function<void()> load_function ;
};

namespace sf
{
    struct texture
    {
        bool load_from_file( std::string /*path*/, int /*flags*/ )
        { return std::cout << "sf::texture::load_from_file(std::string,int)\n" ; }

        bool load_from_memory( const void* /*pointer*/, std::size_t /*nbytes*/, int /*hint*/ )
        { return std::cout << "sf::texture::load_from_memory(const void*,std::size_t,int)\n" ; }
        // ...
    };

    struct font
    {
        void load_from_file( std::string /*path*/ )
        { std::cout << "sf::font::load_from_file(std::string)\n" ; }

        void load_from_stream( std::istream& /*stm*/ )
        { std::cout << "sf::font::load_from_stream(std::istream&)\n" ; }

        bool load_from_font_cache( std::string /*name*/, int /*point_size*/ )
        { return std::cout << "sf::font::load_from_font_cache(std::string,int)\n" ; }
    };
}

void free_fun( sf::texture* tex, std::string tex_path,
                sf::font& fnt, std::istream& fnt_stm )
{
    std::cout << "free_fun( sf::texture*, std::string, sf::font&, std::istream& )\n\t" ;
    tex->load_from_file( tex_path, 0 ) ;
    std::cout << '\t' ;
    fnt.load_from_stream(fnt_stm) ;
}

int main()
{
    sf::texture t ;
    sf::font f ;
    sf::font* pointer = std::addressof(f) ;

    char buffer[128] {} ;
    std::string face_name = "Times Roman Italic" ;
    std::ifstream file( "myfont.fnt" ) ;


    foo a[] =
    {
        // pass object by value (load_from_file is called on copy of t)
        foo( &sf::texture::load_from_file, t, "some_texture.dat", 0 ),

        // pass object by wrapped reference
        foo( &sf::texture::load_from_memory, std::ref(t), buffer, 128, 7 ),

        // pass object by address
        foo( &sf::font::load_from_file, pointer, "myfont.fnt" ),

        // pass parameter by wrapped fererence
        foo( &sf::font::load_from_stream, std::ref(f), std::ref(file) ),

        // pass parameter by wrapped fererence to const
        foo( &sf::font::load_from_font_cache, std::ref(f), std::cref(face_name), 12 ),

        // pass free function
        foo( free_fun, std::addressof(t), "some_texture.dat",
                         std::ref(f), std::ref(file) ),

        // pass closure
        foo( [&] { f.load_from_font_cache( face_name, 20 ) ; } ),

        // pass bind expression
        foo( std::bind( &sf::texture::load_from_file, std::ref(t),
                                     "some_texture.dat", 0 ) ),

        // pass call wrapper
        foo( std::function< void(sf::font&,std::istream&) >(&sf::font::load_from_stream),
                         std::ref(f), std::ref(file) )

    };

    for( foo& f : a ) f.do_load() ;
}

http://coliru.stacked-crooked.com/a/13fee879260ccd65
Topic archived. No new replies allowed.