Tech Off Thread

34 posts

Forum Read Only

This forum has been made read only by the site admins. No new threads or comments can be added.

C++0x (vs2010) question, please tell me what i'm doing wrong

Back to Forum: Tech Off
  • User profile image
    Mr Crash

    Hello fellow C++0x programmers.

     

    I have this code:

    void normal_func(int p)
    {
        _tprintf_s(_T("p: %d\n"), p);
    }
    
    struct normal_struct_func
    {
        inline void operator()(int p) const
        {
            _tprintf_s(_T("p: %d\n"), p);
        }
    };
    
    template<typename T, typename Function>
    void test_f(T p, Function func)
    {
        func(p);
    }
    
    template<typename T, typename Function>
    class test_c {
    public:
        explicit test_c(T p, Function func) : m_p(p), m_Func(func) {}
    
        ~test_c(void)
        {
            m_Func(m_p);
        }
    
        T get(void) const
        {
            return m_p;
        }
    
    private:
        T m_p;
        const Function m_Func;
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        // test_f tests
        test_f(11, [](int p){_tprintf_s(_T("p: %d\n"), p);});
        test_f(22, normal_func);
        test_f(33, normal_struct_func());
        
        // test_c tests
        // error C2976: 'test_c' : too few template arguments
        //test_c<int> tc1(11, [](int p){_tprintf_s(_T("p: %d\n"), p);});
        //test_c<int> tc2(22, normal_func);
        //test_c<int> tc3(33, normal_struct_func());
    
        // error C2664: 'test_c<T,Function>::test_c(T,Function (__cdecl *))' : cannot convert parameter 2 from '`anonymous-namespace'::<lambda1>' to 'void (__cdecl *)(int)'
        // ...
        test_c<int, void(int)> tc1(11, [](int p){_tprintf_s(_T("p: %d\n"), p);});
        test_c<int, void(int)> tc2(22, normal_func);
        test_c<int, void(int)> tc3(33, normal_struct_func());
    
        return 0;
    }
    

     

    The 'test_f' works as it should but test_c class, which does the same basically, does not.

     

    Problem list:

    1. How to make this compile. What am i missing / doing wrong.

    2. Why do i have to type "test_c<int, void(int)>" instead of just "test_c<int>", the compiler should be able to figure that out, it did that for "test_f"

     

    Thanks for your help.

     

  • User profile image
    Dexter

    Why do i have to type "test_c<int, void(int)>" instead of just "test_c<int>"

     

    It works for test_f due to "template argument deduction". This applies only to template functions, not to template classes.

     

    How to make this compile. What am i missing / doing wrong.

     

    Don't use void(int). That simply means a function that takes an int and returns nothing. normal_struct_func is not a function, it's an object that has a () operator aka function object, functor etc. You could use std::function (make sure you include <functional>):

     

    test_c<int, std::function<void(int)>> tc3(33, normal_struct_func());

  • User profile image
    Mr Crash

    > "template argument deduction". This applies only to template functions, not to template classes.

    I need to refresh my C++ knowledge.
    I find it strange and kind of annoying that "template argument deduction" doesn't work for template classes.
    I wonder what the reason was for this rule..

    Yes, i did find that 'std::function' made it work but thought there must be a better way, lack of "template argument deduction" explains it.

    What i'm trying to do is to create a template class that does stuff when it leaves scope:
    Some unoptimized examples:
    1:
    scoped<HMODULE> dll(LoadLibrary("kernel32.dll"), [](HMODULE h){FreeLibrary(h);})

    2:
    scoped<int*> ptr(new int, [](int *p){delete p;})
    3:
    scoped<int[]> ptr(new int[100], [](int *p){delete [] p;})
    4:

    scoped<int> ptr(1974, [](int p){ cout << p << endl;})


    stuff like that.
    I know of unique_ptr but i wanted to create a single template class that could do all of the above.

    The class was all good with pointers and arrays but when i tried to make it do stuff like example 1 i hit a brick wall because i want it to work with all functor, see "test_f tests" for example.

    It seems to be very tricky to get it to work.
    One of the things i would like to minimize is the amount of typing required but the "template argument deduction" not for template classes rule, is in the way of that. auto keyword does n't help either.

    Input, ideas, etc. are much appreciated.

  • User profile image
    Dexter

    In this particular case it seems to me that the simplest solution is to get rid of the Function template argument:

    template<typename T> 
    class test_c {
    public:    
        explicit test_c(T p, std::function<void(T)> func) 
            : m_p(p), m_Func(func) {
        }     
        
        ~test_c(void) {        
            m_Func(m_p);
        }
        
        T get(void) const {        
            return m_p;
        }
    
    private:    
        T m_p;
        const std::function<void(T)> m_Func;
    }; 
    

  • User profile image
    Burkholder

    @Mr Crash: For your specific problem, I like Dexter's way better; however, since stuff like this pops up occassionally, here is another way:

    #include <iostream>
    #include <memory>
    #include <functional>
    
    #include <windows.h>
    
    using namespace std;
    
    //=========================================================
    
    /*
    // Dexter's code:
    template<typename T>  
    class test_c { 
    public:  
        explicit test_c(T p, std::function<void(T)> func)  
            : m_p(p), m_Func(func) { 
        }      
          
        ~test_c(void) {         
            m_Func(m_p); 
        } 
          
        T get(void) const {         
            return m_p; 
        } 
      
    private:     
        T m_p; 
        const std::function<void(T)> m_Func; 
    };
    */
    
    // A dumbed-down version of the functors presented around
    // page 110 of Modern C++ Design by Andrei Alexandrescu:
    template < typename RESULT_TYPE, typename PARAMETER_TYPE >
    class functor_base_t {
        public:
            virtual ~functor_base_t () {
                //
            }
            virtual RESULT_TYPE operator () ( PARAMETER_TYPE ) = 0;
    };
    
    template < typename RESULT_TYPE, typename PARAMETER_TYPE, typename FUNCTOR >
    class functor_handler_t : public functor_base_t< RESULT_TYPE, PARAMETER_TYPE > {
        FUNCTOR m_functor;
        public:
            functor_handler_t ( FUNCTOR functor ) : m_functor( functor ) {
                //
            }
            virtual ~functor_handler_t () {
                //
            }
            virtual RESULT_TYPE operator () ( PARAMETER_TYPE parameter ) {
                return m_functor( parameter );
            }
    };
    
    template < typename TYPE >
    class test_c {
        private:
            typedef void RESULT_TYPE;
            typedef TYPE PARAMETER_TYPE;
    
            PARAMETER_TYPE m_parameter;
            std::shared_ptr< functor_base_t< RESULT_TYPE, PARAMETER_TYPE > > m_functor_handler;
        public:
            template < typename FUNCTOR >
            explicit test_c ( PARAMETER_TYPE parameter, FUNCTOR functor ) :
                m_parameter( parameter ),
                m_functor_handler(
                    new functor_handler_t<
                        RESULT_TYPE,
                        PARAMETER_TYPE,
                        FUNCTOR
                    >( functor )
                )
            {
                //
            }
            ~test_c () {
                ( *m_functor_handler )( m_parameter );
            }
            PARAMETER_TYPE const & get () const {
                return m_parameter;
            }
    };
    
    //=========================================================
    
    inline void normal_func ( int p ) {
        cout << "p: " << p << endl;
    }
    
    struct normal_struct_func {
        inline void operator() (int p) const {
            cout << "p: " << p << endl;
        }
    };
    
    template< typename T, typename Function >
    inline void test_f ( T p, Function func ) {
        func( p );
    }
    
    int main () {
        // test_f tests
        test_f( 11, [] ( int p ) { cout << "p: " << p << endl; } );
        test_f( 22, normal_func );
        test_f( 33, normal_struct_func() );
    
        // test_c tests
        test_c< int > tc1( 11, [] ( int p ) { cout << "p: " << p << endl; } );
        test_c< int > tc2( 22, normal_func );
        test_c< int > tc3( 33, normal_struct_func() );
    
        test_c< int * > tc4(
            new int,
            [] ( int * ptr ) {
                cout << "tc4: " << *ptr << endl;
                delete ptr;
            }
        );
        *tc4.get() = 44;
        test_c< int * > tc5(
            new int[ 3 ],
            [] ( int * arr ) {
                int const * i = &arr[ 0 ];
                int const * end = &arr[ 3 ];
                cout << "tc5: { ";
                if ( i != end ) {
                    cout << *i;
                    for ( ++i ; i != end; ++i )
                        cout << ", " << *i;
                }
                cout << " }" << endl;
                delete[] arr;
            }
        );
        tc5.get()[ 0 ] = 55;
        tc5.get()[ 1 ] = 56;
        tc5.get()[ 2 ] = 57;
    
    #define scoped test_c
    
        // 1:
        scoped<HMODULE> dll(LoadLibraryA("kernel32.dll"), [](HMODULE h){FreeLibrary(h);});
    
        // 2:
        scoped<int*> ptr2(new int, [](int *p){delete p;});
    
        // 3:
        scoped<int*> ptr3(new int[100], [](int *p){delete[] p;});
    
        // 4:
        scoped<int> ptr4(1974, [](int p){ cout << p << endl;});
    
        return 0;
    }
    
    This works in VC++2010 and MinGW's port of g++ 4.5.2.  Here's the command line for MinGW's g++ 4.5.2:
    g++ -o main.exe main.cpp -std=c++0x -march=native -O3 -Wall -Wextra -Werror
    This code produces the following output:
    p: 11
    p: 22
    p: 33
    1974
    tc5: { 55, 56, 57 }
    tc4: 44
    p: 33
    p: 22
    p: 11

    Hope This Also Helps,
    Joshua Burkholder

  • User profile image
    Mr Crash

    Very interesting and helpful answers, thank you.

     

    I decided to see how many times i could shot myself in the foot.
    Basically if i could make something that was lighter then the suggestions.

    That is without std::shared_ptr and std::function.

    And yet again i managed to get into a fight with the compiler

    This is sandbox code so expect strangeness and inconsistencies:


    #include <windows.h> // UNREFERENCED_PARAMETER
    #include <assert.h>
    #include <cstddef>
    #include <utility>
    
    // A macro to disallow the copy constructor and operator= functions
    // This should be used in the private: declarations for a class
    #define DISALLOW_COPY_AND_ASSIGN(TypeName) \
        TypeName(const TypeName&);               \
        void operator=(const TypeName&)
    
    // default_delete - scalar
    template<typename T>
    struct default_delete
    {
        inline void operator()(T obj) const
        {
            // empty
        }
    };
    
    // default_delete - scalar pointer
    template<typename T>
    struct default_delete<T*>
    {
        inline void operator()(T *ptr) const
        {
            delete ptr;
        }
    };
    
    // default_delete - array
    template<typename T>
    struct default_delete<T[]>
    {
        inline void operator()(T *ptr) const
        {
            delete [] ptr;
        }
    };
    
    template<typename T>
    struct default_empty {
        inline void operator()(T) const    {
            // empty
        }
    };
    
    // scalar version
    template<typename T, typename OnLeaveScopeFunc>
    class scoped {
    public:
        explicit scoped(T p = 0, OnLeaveScopeFunc func = default_delete<T>) : m_obj(p), m_onLeaveScope(func) {}
    
        ~scoped(void)
        {
            m_onLeaveScope(m_obj);
        }
    
        // Common to both pointer and array
        void reset(T p = 0)
        {
            if (m_obj != p)
            {
                m_onLeaveScope(m_obj);
                m_obj = p;
            }
        }
    
        T get(void) const
        {
            return m_obj;
        }
    
        bool operator==(const T p) const
        {
            return m_obj == p;
        }
    
        bool operator!=(const T p) const
        {
            return !(m_obj == p);
        }
    
        // Move constructor and assignment
        // This will enable make_scoped to work
        scoped(scoped &&right)
        {
            m_obj = right.m_obj;
            m_onLeaveScope = right.m_onLeaveScope;
            right.m_obj = 0;
        }
    
        scoped &operator=(scoped &&right)
        {
            scoped(std::move(right)).swap(*this); // Invoke move constructor and swap
            return *this;
        }
    
        void swap(scoped &right)
        {
            std::swap(m_obj, right.m_obj);
            std::swap(m_onLeaveScope, right.m_onLeaveScope);
        }
    
        T release(void)
        {
            T temp = m_obj;
            m_obj = 0;
            return temp;
        }
    
    private:
        T m_obj;
        OnLeaveScopeFunc m_onLeaveScope;
    
        DISALLOW_COPY_AND_ASSIGN(scoped);
    };
    
    // scalar pointer version
    template<typename T, typename OnLeaveScopeFunc>
    class scoped<T*, OnLeaveScopeFunc> {
    public:
        typedef T element_type;
    
        explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>) : m_ptr(p), m_onLeaveScope(func), m_onLeaveScope_empty(default_empty<T>) {}
    
        ~scoped(void)
        {
            m_onLeaveScope(m_ptr);
        }
    
        // Common to both pointer and array
        void reset(T *p = nullptr)
        {
            if (m_ptr != p)
            {
                m_onLeaveScope(m_ptr);
                m_ptr = p;
            }
        }
    
        T *get(void) const
        {
            return m_ptr;
        }
    
        bool operator==(const T *p) const
        {
            return m_ptr == p;
        }
    
        bool operator!=(const T *p) const
        {
            return !(m_ptr == p);
        }
    
        // Move constructor and assignment
        // This will enable make_scoped to work
        scoped(scoped &&right)
        {
            m_ptr = right.m_ptr;
            m_onLeaveScope = right.m_onLeaveScope;
            right.m_ptr = nullptr;
            right.m_onLeaveScope = m_onLeaveScope_empty;
        }
    
        scoped &operator=(scoped &&right)
        {
            scoped(std::move(right)).swap(*this); // Invoke move constructor and swap
            return *this;
        }
    
        void swap(scoped &right)
        {
            std::swap(m_ptr, right.m_ptr);
            std::swap(m_onLeaveScope, right.m_onLeaveScope);
        }
    
        T *release(void)
        {
            T *temp = m_ptr;
            m_ptr = nullptr;
            return temp;
        }
    
        // Scalar pointer version
        T &operator*(void) const
        {
            assert(m_ptr != nullptr);
            return *m_ptr;
        }
    
        T *operator->(void) const
        {
            assert(m_ptr != nullptr);
            return m_ptr;
        }
    
    private:
        T *m_ptr;
        OnLeaveScopeFunc m_onLeaveScope;
        OnLeaveScopeFunc m_onLeaveScope_empty;
    
        DISALLOW_COPY_AND_ASSIGN(scoped);
    };
    
    // array version
    // FIXME: Any way to prevent missing [] from compiling ? Ex. scoped<int> bla(new int[])
    // or to detect it based on how it is used further down the code ?
    template<typename T, typename OnLeaveScopeFunc>
    class scoped<T[], OnLeaveScopeFunc> {
    public:
        typedef T element_type;
    
        explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>) : m_ptr(p), m_onLeaveScope(func) {}
    
        ~scoped(void)
        {
            m_onLeaveScope(m_ptr);
        }
    
        // Common to both pointer and array
        void reset(T *p = nullptr)
        {
            if (m_ptr != p)
            {
                m_onLeaveScope(m_ptr);
                m_ptr = p;
            }
        }
    
        T *get(void) const
        {
            return m_ptr;
        }
    
        bool operator==(const T *p) const
        {
            return m_ptr == p;
        }
    
        bool operator!=(const T *p) const
        {
            return !(m_ptr == p);
        }
    
        // Move constructor and assignment
        // This will enable make_scoped to work
        scoped(scoped &&right)
        {
            m_ptr = right.m_ptr;
            m_onLeaveScope = right.m_onLeaveScope;
            right.m_ptr = nullptr;
        }
    
        scoped &operator=(scoped &&right)
        {
            scoped(std::move(right)).swap(*this); // Invoke move constructor and swap
            return *this;
        }
    
        void swap(scoped &right)
        {
            std::swap(m_ptr, right.m_ptr);
            std::swap(m_onLeaveScope, right.m_onLeaveScope);
        }
    
        T *release(void)
        {
            T *temp = m_ptr;
            m_ptr = nullptr;
            return temp;
        }
    
        // Array version
        // Should we use uintptr_t instead of ptrdiff_t ?
        T &operator[](std::ptrdiff_t i) const
        {
            assert(i >= 0);
            assert(m_ptr != nullptr);
            return m_ptr[i];
        }
    
    private:
        T *m_ptr;
        OnLeaveScopeFunc m_onLeaveScope;
    
        DISALLOW_COPY_AND_ASSIGN(scoped);
    };
    
    // template argument deduction goodness
    template <typename T, typename F>
    inline scoped<T, F> make_scoped(T t, F f)
    {
        return std::move(scoped<T, F>(t, f));
    }
    
    //----------------------------------------------------
    
    inline void normal_f(int p) {
        _tprintf_s(_T("normal_f p: %d\n"), p);
    }
    
    struct normal_struct_f {
        inline void operator()(int p) const {
            _tprintf_s(_T("normal_struct_f p: %d\n"), p);
        }
    };
    
    inline void normal_fp(int *p) {
        _tprintf_s(_T("normal_fp *p: %d\n"), *p);
    }
    
    struct normal_struct_fp {
        inline void operator()(int *p) const {
            _tprintf_s(_T("normal_struct_fp *p: %d\n"), *p);
        }
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        UNREFERENCED_PARAMETER(argc);
        UNREFERENCED_PARAMETER(argv);
    
        // 2010-02-24
        auto f = [](int p) {
            _tprintf_s(_T("f p: %d\n"), p);
        };
    
        auto fp = [](int *p) {
            _tprintf_s(_T("fp *p: %d\n"), *p);
        };
    
        {
            auto t1 = make_scoped(1975, f);
            auto t2 = make_scoped(1975, normal_f);
            auto t3 = make_scoped(1975, normal_struct_f());
        }
        _tprintf_s(_T("\n")); // For output readability
        {
            auto t1 = make_scoped(new int(1975), fp);
            auto t2 = make_scoped(new int(1975), normal_fp);
            auto t3 = make_scoped(new int(1975), normal_struct_fp());
        }
        _tprintf_s(_T("\n")); // For output readability
        return 0;
    }
    


    Ok, so the problem i'm having:
    In <T*> version of 'scoped(scoped &&right)' (that's the class i've used as test subject)
    How to make 'right.m_onLeaveScope' accept the new empty function ?


    The compiler complains:
    "error C2275: 'default_empty<T>' : illegal use of this type as an expression"
    due to 'm_onLeaveScope_empty(default_empty<T>)' on line
    explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>) : m_ptr(p), m_onLeaveScope(func), m_onLeaveScope_empty(default_empty<T>) {}

    I can't see why i doesn't want to work.

    "OnLeaveScopeFunc func = default_delete<T>"

    Worked fine so why doesn't that want to work ?

    I've also tried:

    "OnLeaveScopeFunc func_empty = default_empty<T>"

    'm_onLeaveScope_empty(func_empty)'

     

    explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>, OnLeaveScopeFunc func_empty = default_empty<T>) : m_ptr(p), m_onLeaveScope(func), m_onLeaveScope_empty(func_empty) {}

  • User profile image
    Dexter

    You're trying to assign a type name (default_delete<T>) to a variable (m_onLeaveScope_empty), you forgot a ():

    m_onLeaveScope_empty(default_empty<T>()) 

    It worked in the case of the func argument because you never use the default value in your tests. As soon as you write something like scoped<int, decltype(f)> f23(23); you'll get the same error. 

     

    Anyway, it still won't work. Once you fix this you'll get another error: '`anonymous-namespace'::<lambda1>::(const `anonymous-namespace'::<lambda1> &)' : cannot convert parameter 1 from 'default_empty<T>' to 'const `anonymous-namespace'::<lambda1> &'

     

    Again, you're mixing up things that look like functions but they have different types. In the particular case that triggered the above error your deleter is a lambda and the default_empty is a function object. They're not compatible.

  • User profile image
    Mr Crash

    > Anyway, it still won't work. Once you fix this you'll get another error: '`anonymous-namespace'::<lambda1>::(const `anonymous-namespace'::<lambda1> &)' : cannot convert parameter 1 from 'default_empty<T>' to 'const `anonymous-namespace'::<lambda1> &'

     

    I knew i forgot to mention something, it was 3 am when i wrote that, sorry about that.

    I did get that error too, which made me confused and got me thinking i've forgot some c++ rule again.

     

    > Again, you're mixing up things that look like functions but they have different types.

     

    Yes, can you explain what i'm missing, please ?

     

    How would i make it work or is that not a possibility without std::function ?

  • User profile image
    Dexter

    Yes, can you explain what i'm missing, please ?

    Let's take the case of make_scoped(new int(1975), normal_struct_fp()). This creates a scoped<int, normal_struct_fp> where m_onLeaveScope_empty is of type normal_struct_fp. And the scoped constructor is trying to assign an object of type default_empty<int> to a variable of type normal_struct_fp. That won't work, just because 2 types have an () operator with the same signature it doesn't mean that you can convert from one type to another.

    How would i make it work or is that not a possibility without std::function?

    Well, it seems to me that the only purpose of m_onLeaveScope_empty is to support the move constructor. You could leave the old m_onLeaveScope alone and add a null check in the scoped destructor so the old m_onLeaveScope doesn't get called.

  • User profile image
    Mr Crash

    , Dexter wrote

    Well, it seems to me that the only purpose of m_onLeaveScope_empty is to support the move constructor. You could leave the old m_onLeaveScope alone and add a null check in the scoped destructor so the old m_onLeaveScope doesn't get called.

    Yes, I've thought of that, it would work for the pointer version, but not for the non pointer version.

    We can't really test for m_obj != 0 since 0 might be a totally valid value.

     

    The possible solution would be to add a bool that gets set to true (is_empty = true) when the data is moved.

    This, i'm guessing, would also be faster then calling an empty function.

     

    But i'm not sure which way to go. I want it to be correct and as performance as possible so i'm still wondering if there is some kind of  template magic to sole this.

     

    But i'm leaning to the bool solution or perhaps i should use std::function...

    Am i overthinking it ?

  • User profile image
    Dexter

    Yep, the non pointer version is an issue, didn't look at it as it didn't have compile errors Smiley

    Performance wise I'd say that the bool variant is the fastest. std::function is cleaner but it has larger time & space overhead. But you know what they say, premature optimization is the root of all evil. Things like CreateFile or LoadLibrary will take far more time to complete than the few instructions that are need by make_scope/scoped.

    If you really want it to be fast then the first thing you'll want to do is to get rid of make_scoped, it's not "free".

  • User profile image
    Jonas_No

    Interesting topic.
    But the code examples above will generate a lot of overhead for the small tasks it seems to be for. For example using it as a scoped_array.
    Best would be if you could make the optimizer inline , etc..
    Make the compiler do the heavy work for us.

    Like this little code example:

    inline void normal_function(int v) {
        cout << "normal_function: " << v << endl;
    }
    
    template <typename Function>
    class sop {
        Function f_;
    public:
        sop(Function fun) : f_(fun) {}
        ~sop() {
            f_();
        }
    };
    
    void t_sop()
    {
        auto l = [](){
            normal_function(123);
        };
        sop<decltype(l)> s(l);
    }
    
    int main()
    {
        t_sop();
        return 0;
    }
     

     

    Generated code in win32 release mode:

        t_sop();
    00291000  mov         eax,dword ptr [__imp_std::endl (292044h)]  
    00291005  mov         ecx,dword ptr [__imp_std::cout (292068h)]  
    0029100B  push        eax  
    0029100C  push        7Bh  
    0029100E  push        offset string "normal_function: " (292114h)  
    00291013  push        ecx  
    00291014  call        std::operator<<<std::char_traits<char> > (2910F0h)  
    00291019  add         esp,8  
    0029101C  mov         ecx,eax  
    0029101E  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (292050h)]  
    00291024  mov         ecx,eax  
    00291026  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (29204Ch)]  
    


    Shouldn't you be able to achieve this with the power of c++0x but without this big overhead ?

  • User profile image
    Dexter

    Best would be if you could make the optimizer inline , etc..

    I don't understand... that's exactly what the compiler did. The code in main is the code from normal_function, 3 calls to the stream insertion operator and nothing else.

  • User profile image
    Jonas_No

    @Dexter: The code in my post was just an example of compiler optimization. How a good template class should work.

    Both your code and Burkholder's code have a big overhead.
    Is there a way to minimize the overhead like in my example ? 

  • User profile image
    Dexter

    OK, I understand now. I don't know of a way to remove this overhead, std::function is horrible complex and the compiler has little chance to optimize it.

    It seems to me that if you don't provide the function type in the template argument list (like in the original code or in your code) then the only alternative is to use std::function or something that does something similar: wraps a callable object. And this wrapping is complex because there are different types of callable objects: pointer to function, pointer to member, functors.

  • User profile image
    new2STL

    I posted this link on last Advanced STL show: http://blog.tomaka17.com/2011/01/some-interesting-usages-of-stdfunction/

    here an except of the code (how to register a function to call)

    template <typename TEvent, typename TListener >
    void _registerListener (const TListener& function) {
    static_assert(!std::is_array<TEvent>::value, "You cannot register an array as an event type");
    static_assert(std::is_convertible<TListener,std::function<void(TEvent&)>>::value, "Unvalid callback for this event type");
    _listeners.insert(std::make_pair(&typeid(std::decay<TEvent>::type), [=](void* ev) { function(*static_cast<TEvent*>(ev)); }));
    }

    is_convertible and decay seens what you are looking for deal with functors, function and lambdas. There are other interesting stuff on the blog. (I think I see an example with std::bind for additional "magic"call somewhere, I'll try re-digg it)

  • User profile image
    Jonas_No
  • User profile image
    Mr Crash

    STL's ScopeWarden class looks promising.
    I'm a bit disappointed that c++ is limited in the area of functor and lambda storing.
    I wonder why they have not done anything about it.
    Is it not common enough or do people just bite the bullet and take the dynamic allocation overhead and hope the cpu's get faster next year ? 

    Somebody from the c++ group should really comment on this.

Conversation locked

This conversation has been locked by the site admins. No new comments can be made.