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
    STL

    What is "limited"?

  • User profile image
    Mr Crash

    @STL:Limited as in not being able to store functors and lambdas without overhead or workaround (you called it new trickery) "You need placement new trickery for arbitrary functors." 

    Was that what you were asking ? You're question is vague, can you rephrase the question ?

    I absolutely love c++ but it do have it's flaws like this one which i think is one of those things that have been overlooked.

  • User profile image
    Mr Crash

    Well well, i've found something that looks good and doesn't use a marco, sad part, vs2010 cant handle it Sad

    http://pizer.wordpress.com/2008/11/22/scope-guards-revisited-c0x-style/#more-188

    Arr where's that vs2011 beta ?!

  • User profile image
    STL

    > Limited as in not being able to store functors and lambdas without overhead or workaround

    std::function encapsulates that machinery. It inherently has nonzero cost, and inherently must throw exceptions for arbitrarily large functors.

    There is no flaw to solve here.

  • User profile image
    Mr Crash

    @STL: std:function still have overhead which makes it less usable for doing small things. Like using in a scope guard class or special callbacks, etc..

    Take this code and compile it in release mode / win32 and look at it in debugging.

    #include <iostream>
    #include <functional>
    
    using namespace std;
    
    inline void func() {
        cout << "func" << endl;
    }
    
    struct s_func {
        inline void operator()() {
            cout << "s_func" << endl;
        }
    };
    
    template <typename Functor>
    void test_template(Functor f) {
        f();
    }
    
    void test_function(function<void()> f) {
        f();
    }
    
    int main()
    {
        test_template(func);
        test_function(func);
    
        test_template(s_func());
        test_function(s_func());
    }
    

    test_template(func):

        test_template(func);
    00301095  mov         eax,dword ptr [__imp_std::endl (302044h)]  
    0030109A  mov         ecx,dword ptr [__imp_std::cout (302068h)]  
    003010A0  push        eax  
    003010A1  push        offset string "func" (30214Ch)  
    003010A6  push        ecx  
    003010A7  call        std::operator<<<std::char_traits<char> > (301240h)  
    003010AC  add         esp,8  
    003010AF  mov         ecx,eax  
    003010B1  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (30204Ch)]  

    test_function(func); : (called functions not included)

        test_function(func);
    003010B7  mov         edx,offset func (301040h)  
    003010BC  test        edx,edx  
    003010BE  jne         main+54h (3010C4h)  
    003010C0  xor         ecx,ecx  
    003010C2  jmp         main+65h (3010D5h)  
    003010C4  mov         dword ptr [ebp-24h],offset std::tr1::_Impl_no_alloc0<std::tr1::_Callable_fun<void (__cdecl*const)(void),0>,void>::`vftable' (302188h)  
    003010CB  mov         dword ptr [ebp-20h],offset func (301040h)  
    003010D2  lea         ecx,[ebp-24h]  
    003010D5  mov         dword ptr [ebp-14h],ecx  
    003010D8  mov         dword ptr [ebp-4],0  
    003010DF  test        ecx,ecx  
    003010E1  jne         $LN163 (3010E9h)  
    003010E3  call        dword ptr [__imp_std::tr1::_Xfunc (302064h)]  
    $LN163:
    003010E9  mov         eax,dword ptr [ecx]  
    003010EB  mov         edx,dword ptr [eax+4]  
    003010EE  call        edx  
    003010F0  mov         dword ptr [ebp-4],0FFFFFFFFh  
    003010F7  mov         ecx,dword ptr [ebp-14h]  
    003010FA  test        ecx,ecx  
    003010FC  je          $LN163+28h (301111h)  
    003010FE  mov         eax,dword ptr [ecx]  
    00301100  mov         eax,dword ptr [eax+0Ch]  
    00301103  lea         edx,[ebp-24h]  
    00301106  cmp         ecx,edx  
    00301108  setne       dl  
    0030110B  movzx       edx,dl  
    0030110E  push        edx  
    0030110F  call        eax  

    function 'test_template' is a clear winner. Now the sad part is that you can't do this in a class without overhead because you have to store the function object which isn't really supported so you have to resort to tricks which have overhead.

    // class version equivalent to test_template
    // This should have been supported but nope
    class scope_guard {
        auto f;
    public:
        template <typename Functor>
        scope_guard(Functor f_obj) : f(f_obj) { // <- ???
        }
        ~scope_guard() {
            f();
        }
    

    function "test_template" works because you don't have to store the function object

  • User profile image
    Sven Groot

    How about this:

    template<typename Functor>
    class scope_guard
    {
    public:
        explicit scope_guard(Functor &f) throw()
            : _f(std::addressof(f)) 
        { 
        }
    
        ~scope_guard() throw()
        {
            if( _f != nullptr )
            {
                try
                {
                    (*_f)();
                }
                catch(...)
                {
                    std::terminate();
                }
            }
        }
    
        scope_guard(scope_guard<Functor> &&other) throw()
            : _f(other._f)
        {
            other._f = nullptr;
        }
    
        scope_guard<Functor>& operator=(scope_guard<Functor> &&other) throw()
        {
            if( &other != this )
            {
                _f = other._f;
                other._f == nullptr;
            }
            return *this;
        }
    private:
        scope_guard(const scope_guard<Functor>&);
        scope_guard<Functor>& operator=(const scope_guard<Functor>&);
    
        Functor *_f;
    };
    
    template<typename Functor>
    inline scope_guard<Functor> create_scope_guard(Functor &f) throw()
    {
        return scope_guard<Functor>(f);
    }

    This makes the scope_guard non-copyable but movable, so it can be returned by value without triggering the action of desctructor more than once. This allows the create_scope_guard function to do the template parameter deduction for you.

    Use as:

    auto guard = create_scope_guard([]() { cout << "foo" << endl; });

    It's not entirely your syntax, but it's not too bad, I think. Smiley

    It's essentially like STL's class but using a move constructor and a function in place of the macro.

    Note that due to the behavior of copy elision in VC++ the move constructor isn't strictly necessary. VC++ lets you return a non-copyable object by value if it can perform copy elision. By contrast, g++ will not allow you to return an object by value without a public copy or move constructor, even if neither would be called due to copy elision.

  • User profile image
    Jonas_No

    @Sven Groot: Thanks for joining the discussion.

    I remember doing something similar. The problem was the same as in your suggestion.

    It doesn't work for plain functions, in your case it because of "std::addressof".

    I remember trying to use some template magic, traits, is_function<..>, conditional, enable_if,  to make the compiler choose between two version but i failed miserably.

    The compiler just didn't want to play ball. Either it's due to bugs in the compiler or me missing some small detail.

    I just could figure out why it didn't work. Not even g++'s more helpful error messages helped me.

    About 'Mr Crash' auto in class pseudo code: Would have been sweet if auto worked in classes like that. It would have reduced the pain of some template programming though it would surely cause other problems.

  • User profile image
    Jonas_No


    Please tell me how bad this code is:

     

    template<typename Functor>
    class scope_guard {
    public:
        explicit scope_guard(Functor &f, false_type) throw()
            : _f(addressof(f))
        {}
    
        explicit scope_guard(Functor &f, true_type) throw()
            : _f(f)
        {}
    
        ~scope_guard() throw()
    
        {
            if( _f != nullptr )
            {
                try
                {
                    (*_f)();
                }
                catch(...)
                {
                    std::terminate();
                }
            }
        }
    
        scope_guard(scope_guard<Functor> &&other) throw()
            : _f(other._f)
        {
            other._f = nullptr;
        }
    
        scope_guard<Functor>& operator=(scope_guard<Functor> &&other) throw()
        {
            if( &other != this )
            {
                _f = other._f;
                other._f == nullptr;
            }
            return *this;
        }
    private:
        scope_guard(const scope_guard<Functor>&);
        scope_guard<Functor>& operator=(const scope_guard<Functor>&);
    
        Functor *_f;
    };
    
    template<typename Functor>
    inline scope_guard<Functor> create_scope_guard(Functor &f) throw()
    {
        return scope_guard<Functor>(f, typename is_function<Functor>::type());
    }
    
    inline void noarg_func( void ) {
        cout << "noarg_func" << endl;
    }
    struct noarg_functor {
        inline void operator()( void ) const {
            cout << "noarg_functor" << endl;
        }
    };
    
    void test() {
        auto guard1 = create_scope_guard([](){cout << "noarg_lambda" << endl;});
        auto guard2 = create_scope_guard(noarg_functor());
        auto guard3 = create_scope_guard(noarg_func); // <- Doesn't get inlined
    }
    
    int main()
    {
        test();
    }
    

     

    Only problem is that this class use of " C4239: nonstandard extension used ..."
    which is very bad 

  • User profile image
    Sven Groot

    @Jonas_No:

    I came up with this, which uses an enable_if selected template function to pick how to get the address of the function.

    template<typename Functor>
    class scope_guard
    {
    public:
        scope_guard(Functor &f) throw()
            : _f(addressof(f)) 
        {
        }
    
        ~scope_guard() throw()
        {
            if( _f != nullptr )
            {
                try
                {
                    (*_f)();
                }
                catch(...)
                {
                    std::terminate();
                }
            }
        }
    
        scope_guard(scope_guard<Functor> &&other) throw()
            : _f(other._f)
        {
            other._f = nullptr;
        }
    
        scope_guard<Functor>& operator=(scope_guard<Functor> &&other) throw()
        {
            if( &other != this )
            {
                _f = other._f;
                other._f == nullptr;
            }
            return *this;
        }
    private:
        template<typename T>
        static typename std::enable_if<std::is_function<T>::value, T*>::type addressof(T &value)
        {
            return &value;
        }
    
        template<typename T>
        static typename std::enable_if<!std::is_function<T>::value, T*>::type addressof(T &value)
        {
            return std::addressof(value);
        }
    
        scope_guard(const scope_guard<Functor>&);
        scope_guard<Functor>& operator=(const scope_guard<Functor>&);
    
        Functor *_f;
    };
    
    template<typename Functor>
    inline scope_guard<Functor> create_scope_guard(Functor &f) throw()
    {
        return scope_guard<Functor>(f);
    }

    It works in VC++, but g++ is being more difficult, particularly because it doesn't support std::addressof, but also because it can't bind a lambda to a non-const reference parameter.

  • User profile image
    Jonas_No

    This makes the warnings go away in vs2010 but g++ is smarter then vs2010 it, lol, and gives an error.

    template<typename Functor>
    inline scope_guard<Functor> create_scope_guard(const Functor &f) throw()
    {
        return scope_guard<Functor>(const_cast<Functor&>(f));
    }

    C:\test>g++ -Wall -Wextra -std=c++0x -o main.exe test.cpp
    test.cpp: In function 'scope_guard<Functor> create_scope_guard(const Functor&) [with Functor = void()]':
    test.cpp:100:45:   instantiated from here
    test.cpp:85:53: error: invalid use of const_cast with type 'void (&)()', which is a pointer or reference to a function type
    test.cpp:86:1: warning: control reaches end of non-void function
    

  • User profile image
    Sven Groot

    Okay, I've been doing some reading of N3242, and what VC++ is doing is wrong. The version of the class I presented should not work.

    Both my version and STL's version are initialized using a non-const reference, which should not be able to bind to an rvalue. According to N3242, the result of a lambda expression is a pure rvalue, so you should not be able to call that constructor (or the create_scope_guard) using a lambda. STL's macro gets around that by storing the lambda in a local variable first, so he's not passing an rvalue to the function.

    And come to think of it, using "create_scope_guard(noarg_functor());" shouldn't work either, because a temporary shouldn't bind to an non-const reference either. What's worse, I'm storing the address of that temporary, which is destructed as soon as create_scope_guard returns, so when the scope_guard goes out of scope it will try to use an already destructed object! Again, this doesn't apply to STL's version because if you use the macro, you're never passing a functor to it.

    I find it very peculiar that VC++ lets you bind rvalue temporaries to non-const references, even though the standard explicitly forbids that, even in C++03.

    Despite circumventing this issue (as long as you use the macro), STL's ScopeWarden class still won't work in g++ of course, due to the use of std::addressof (not yet supported) and __declspec(nothrow) (not standard).

    Therefore, I believe the only way to make this safe to use on function pointers, functors and lambdas is to copy the function object by value. Yes, this may be problematic if the functor is large or has a non-trivial copy ctor, but I don't think there's another safe, standards-compliant way to do this.

    The following uses this approach, and works in VC2010 and g++ 4.5:

    template<typename Functor>
    class scope_guard
    {
    public:
        scope_guard(Functor function)
            : _function(function), _valid(true)
        {
        }
    
        ~scope_guard() throw()
        {
            if( _valid )
            {
                try
                {
                    _function();
                }
                catch(...)
                {
                    std::terminate();
                }
            }
        }
    
        scope_guard(scope_guard<Functor> &&other)
            : _function(std::move(other._function)), _valid(other._valid)
        {
            other._valid = false;
        }
    
        scope_guard<Functor>& operator=(scope_guard<Functor> &&other)
        {
            if( &other != this )
            {
                _function = std::move(other._function);
                _valid = other._valid;
                other._valid = false;
            }
            return *this;
        }
    private:
        scope_guard(const scope_guard<Functor>&);
        scope_guard<Functor>& operator=(const scope_guard<Functor>&);
    
        Functor _function;
        bool _valid;
    };
    
    template<typename Functor>
    inline scope_guard<Functor> create_scope_guard(Functor function)
    {
        return scope_guard<Functor>(function);
    }

  • User profile image
    STL

    > STL's macro gets around that by storing the lambda in a local variable first, so he's not passing an rvalue to the function.

    Yep, ScopeWarden is designed to invoke named functors (including named lambdas). The macro is just for convenience.

    Because ScopeWarden is templated on functor type, it's physically impossible to give it an unnamed lambda.

    Finally, observe that "explicit ScopeWarden(F&&);" is private and unimplemented. That prevents you, at compile-time, from constructing ScopeWarden<F> from an F rvalue.

    (I am *very* careful.)

    > I find it very peculiar that VC++ lets you bind rvalue temporaries to non-const references, even though the standard explicitly forbids that, even in C++03.

    This is what I refer to as the Evil Extension. Always compile with /W4, which usually warns about any attempt to invoke the Evil Extension.

    > Despite circumventing this issue (as long as you use the macro), STL's ScopeWarden
    > class still won't work in g++ of course, due to the use of
    > std::addressof (not yet supported)

    Just say "&f". std::addressof() was for pedantic correctness, if someone has a functor that also overloads the address-of operator.

    > and __declspec(nothrow) (not standard).

    Just eliminate it - it's an optimization hint - or replace it with noexcept (GCC 4.6+). With noexcept, you don't need the try-catch-terminate in the dtor.

    ScopeWarden is perfectly portable, I just didn't bother to write the minor ifdefs to make it so.

    By the way - the problem with copying by value is that copy ctors can throw exceptions (what if the functor contains a std::string?). Consider what happens if you write:

    do_X();

    [... scope guard g to undo X, whose construction can throw ...]

    do_Y();

    g.dismiss();

    If g's construction (or anything involved in it, like a helper function) throws, then X will remain done without Y being done.  That violates transaction semantics, and defeats the whole purpose of a scope guard.

  • User profile image
    Sven Groot

    The strange thing is, the existence of the private "explicit ScopeWarden(F&&);" prevents my template function creation approach from working with regular function pointers. If that declaration isn't there, it just uses the regular constructor, but if it is there, I get an error about not being able to access the private member.

    By the way - the problem with copying by value is that copy ctors can throw exceptions

    I realize that, but so can constructors. If I use your ScopeWarden like this:

    SomeFunctor f;ScopeWarden<SomeFunctor> warden(f);

    The constructor for f can still throw an exception, even if the ScopeWarden cannot. Is there any strong guarantee in the standard that construction of a lambda won't throw an exception?

    Personally, if I were to use a class like this for transaction semantics, I'd probably use it like this:

    SCOPE_WARDEN(if( isXDone ) UndoX(););DoX();

    This circumvents the whole issue of throwing exceptions in the ctor.

    This is similar to how C#'s using puts the variable outside the generated try/finally block, but its instantiation inside, and then in the finally block it checks for null before calling Dispose.

    Ultimately the question here is: how would you define your ScopeWarden to be usablewithout a macro, but still require only a single statement to use and not require explicitly stating the template parameter? That's what I've been trying to do (more as a thought exercise rather than for anything practical), and what I think Jonas_No's goal was too.

  • User profile image
    STL

    > The strange thing is, the existence of the private "explicit ScopeWarden(F&&);"
    > prevents my template function creation approach from working with regular function pointers.

    Are you saying &func? That's an rvalue (observe that &(&func) would be bogus).

    ScopeWarden could be partially specialized for function pointers (it's just obnoxious if you want to handle arbitrary calling conventions).

    > The constructor for f can still throw an exception, even if the ScopeWarden cannot.

    Yeah, ScopeWarden can't defend against that. "Don't do that then."

    > Is there any strong guarantee in the standard that construction of a lambda won't throw an exception?

    Lambdas with value captures of throwing types obviously can. The Standard appears to be silent about stateless lambdas, and lambdas with only value captures of non-throwing types (e.g. int) and reference captures, but no sane implementation will ever emit exceptions for those.

    > Personally, if I were to use a class like this for transaction semantics, I'd probably use it like this:
    > SCOPE_WARDEN(if( isXDone ) UndoX()Wink;DoX();

    Ah, but then X needs to record that it's done.

    > This circumvents the whole issue of throwing exceptions in the ctor.

    The SCOPE_WARDEN macro already avoids that.

    > Ultimately the question here is: how would you define your ScopeWarden to be usablewithout
    > a macro, but still require only a single statement to use and not require explicitly
    > stating the template parameter? That's what I've been trying to do (more as a thought
    > exercise rather than for anything practical), and what I think Jonas_No's goal was too.

    I don't think it can be done, and I thought about this for a long time while writing ScopeWarden.

  • User profile image
    Sven Groot

    ,STL wrote

     Are you saying &func? That's an rvalue (observe that &(&func) would be bogus).

    No, I'm doing the following:

    template<typename Functor>class TestClass{public:    TestClass(Functor &f) : _f(&f) { }private:    TestClass(Functor &&); // (*)    Functor *_f;};template<typename Functor>inline TestClass<Functor> Create(Functor &f){    return TestClass<Functor>(f);}void Foo(){}int main(){    auto test = Create(Foo);    return 0;}

    This gives the error message "error C2248: 'TestClass<Functor>::TestClass' : cannot access private member declared in class 'TestClass<Functor>'".

    But if you remove the line marked with (*), it works fine.

    I don't think it can be done, and I thought about this for a long time while writing ScopeWarden.

    I expect you're right, at least without accepting the use of the copy ctor. Of course, you could simply mandate "don't use types that can throw in the copy ctor" and be done with it.

  • User profile image
    Mr Crash

    In other words: c++ is limited in this area.
    Like i said a few posts ago.

    That is still sad to hear. Sad

     

Conversation locked

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