0
\$\begingroup\$

Due to a lack of every standard from C++11 upwards in my IDE I rolled an own litte lambda expression like construct some time ago, which I want to share now. I wouldn't necessarily recommend using this in production code, since #defines aren't the best way to go.

First of all, we need an any class. Mine is called Whatever. It's not the best implementation you can get, but it serves its purpose.

#define public_functions  public
#define public_member     public
#define public_classes    public
#define private_functions private
#define private_member    private
#define private_classes   public

namespace util
{
    namespace exception
    {
        class bad_cast
        {
        public_functions:
            bad_cast()
                : m_Msg("bad_cast: invalid conversion of a Whatever object")
            {}

            const char* what()
            {
                return m_Msg;
            }

        private_member:
            const char* m_Msg;
        };
    }

    class Whatever
    {
    public_classes:
        typedef int TypeID;

    public_functions:
        Whatever()
            : m_WrappedValue(nullptr)
        {}

        Whatever(const Whatever& what)
            : m_WrappedValue(what.m_WrappedValue->copy())
        {}

        Whatever& operator = (const Whatever& what)
        {
            if (nullptr != m_WrappedValue)
            {
                delete m_WrappedValue;
                m_WrappedValue = nullptr;
            }

            m_WrappedValue = what.m_WrappedValue->copy();

            return *this;
        }

        ~Whatever()
        {
            if (nullptr != m_WrappedValue)
            {
                delete m_WrappedValue;
                m_WrappedValue = nullptr;
            }
        }

        template <class T> Whatever(const T& value)
            : m_WrappedValue(new TypeWrapper<T>(value))
        {}

        template<class T> T& operator = (const T& value)
        {
            if (nullptr != m_WrappedValue)
            {
                delete m_WrappedValue;
                m_WrappedValue = nullptr;
            }

            m_WrappedValue = new TypeWrapper<T>(value);

            return static_cast<TypeWrapper<T>*>(m_WrappedValue)->get();
        }

        template <class T> operator T() const
        {
            if (TypeWrapper<T>::getTypeStatic() != m_WrappedValue->getType())
            {
                throw exception::bad_cast();
            }

            return static_cast<TypeWrapper<T>*>(m_WrappedValue)->get();
        }

        template <class T> T& get() const
        {
            if (TypeWrapper<T>::getValueStatic() != m_WrappedValue->getType())
            {
                throw exception::bad_cast();
            }


            return static_cast<TypeWrapper<T>*>(m_WrappedValue)->get();
        }

        TypeID getType() const
        {
            return m_WrappedValue->getType();
        }

    private_classes:
        class BaseWrapper
        {
        public_fuctions:
            virtual TypeID getType() const = 0;

            virtual BaseWrapper* copy() const = 0;

            virtual ~BaseWrapper()
            {}
        };

        template<class T> class TypeWrapper : public BaseWrapper
        {
        public_functions:
            TypeWrapper(const T& value)
                : m_Value(value)
            {
                if (s_TypeIdent == s_DefaultID)
                {
                    s_TypeIdent = s_BaseID;
                    ++s_BaseID;
                }
            }

            T& get()
            {
                return m_Value;
            }

            TypeID getType() const
            {
                return s_TypeIdent;
            }

            static TypeID getTypeStatic()
            {
                return s_TypeIdent;
            }

            BaseWrapper* copy() const
            {
                return new TypeWrapper<T>(m_Value);
            }

        private_member:
            T             m_Value;
            static TypeID s_TypeIdent;
        };

    private_member:
        BaseWrapper*        m_WrappedValue;

        static TypeID       s_BaseID;
        static const TypeID s_DefaultID;
    };

    const Whatever::TypeID Whatever::s_DefaultID = -1;

    Whatever::TypeID Whatever::s_BaseID = 0;
    template <class T> Whatever::TypeID Whatever::TypeWrapper<T>::s_TypeIdent = Whatever::s_DefaultID;
}

And then, we only need a little define which allows us to create lambda-like expressions:

#define lambda(x, n) struct expression##n { util::Whatever operator()x } exp##n

And that's it pretty much. The only downside by this kind of lambda like expression is the fact that we always need a return value. You could resolve this by adding another define with a void function but I simply accepted the fact that I alway have to return at least 0 from my expression.

And here is a litte usage example:

int main()
{
    std::vector<int> numbers = fillMeWithNumbers();

    for (auto iterator = numbers.begin(); numbers.end() != iterator; ++iterator)
    {
        lambda((std::vector<int>::iterator out)
        {
            std::cout << *out; 
            return *out; 
        }, 1);
        exp1(iterator);
    }

    return 0;
}

I know, don't roll your own stuff, but since I weren't able to use "real" lambda expressions I chose to roll my own instead of missing it completely. By now this implementation is obsolete.

Feel free to add any criticism, ideas, problems with my implementation and so on.

enter image description here

enter image description here

(from codepad.remoteinterview)

EDIT

So since Loki is right and GCC had a little problem with the public/private defines, here is the code sample without them:

#include <iostream>
#include <vector>

namespace util
{
    namespace exception
    {
        class bad_cast
        {
        public:
            bad_cast()
                : m_Msg("bad_cast: invalid conversion of a Whatever object")
            {}

            const char* what()
            {
                return m_Msg;
            }

        private:
            const char* m_Msg;
        };
    }

    class Whatever
    {
    public:
        typedef int TypeID;

    public:
        Whatever()
            : m_WrappedValue(nullptr)
        {}

        Whatever(const Whatever& what)
            : m_WrappedValue(what.m_WrappedValue->copy())
        {}

        Whatever& operator = (const Whatever& what)
        {
            if (nullptr != m_WrappedValue)
            {
                delete m_WrappedValue;
                m_WrappedValue = nullptr;
            }

            m_WrappedValue = what.m_WrappedValue->copy();

            return *this;
        }

        ~Whatever()
        {
            if (nullptr != m_WrappedValue)
            {
                delete m_WrappedValue;
                m_WrappedValue = nullptr;
            }
        }

        template <class T> Whatever(const T& value)
            : m_WrappedValue(new TypeWrapper<T>(value))
        {}

        template<class T> T& operator = (const T& value)
        {
            if (nullptr != m_WrappedValue)
            {
                delete m_WrappedValue;
                m_WrappedValue = nullptr;
            }

            m_WrappedValue = new TypeWrapper<T>(value);

            return static_cast<TypeWrapper<T>*>(m_WrappedValue)->get();
        }

        template <class T> operator T() const
        {
            if (TypeWrapper<T>::getTypeStatic() != m_WrappedValue->getType())
            {
                throw exception::bad_cast();
            }

            return static_cast<TypeWrapper<T>*>(m_WrappedValue)->get();
        }

        template <class T> T& get() const
        {
            if (TypeWrapper<T>::getValueStatic() != m_WrappedValue->getType())
            {
                throw exception::bad_cast();
            }


            return static_cast<TypeWrapper<T>*>(m_WrappedValue)->get();
        }

        TypeID getType() const
        {
            return m_WrappedValue->getType();
        }

    private:
        class BaseWrapper
        {
        public:
            virtual TypeID getType() const = 0;

            virtual BaseWrapper* copy() const = 0;

            virtual ~BaseWrapper()
            {}
        };

        template<class T> class TypeWrapper : public BaseWrapper
        {
        public:
            TypeWrapper(const T& value)
                : m_Value(value)
            {
                if (s_TypeIdent == s_DefaultID)
                {
                    s_TypeIdent = s_BaseID;
                    ++s_BaseID;
                }
            }

            T& get()
            {
                return m_Value;
            }

            TypeID getType() const
            {
                return s_TypeIdent;
            }

            static TypeID getTypeStatic()
            {
                return s_TypeIdent;
            }

            BaseWrapper* copy() const
            {
                return new TypeWrapper<T>(m_Value);
            }

        private:
            T             m_Value;
            static TypeID s_TypeIdent;
        };

    private:
        BaseWrapper*        m_WrappedValue;

        static TypeID       s_BaseID;
        static const TypeID s_DefaultID;
    };

    const Whatever::TypeID Whatever::s_DefaultID = -1;

    Whatever::TypeID Whatever::s_BaseID = 0;
    template <class T> Whatever::TypeID Whatever::TypeWrapper<T>::s_TypeIdent = Whatever::s_DefaultID;
}

#define lambda(x, n) struct expression##n { util::Whatever operator()x } exp##n

std::vector<int> fillMeWithNumbers()
{
    std::vector<int> vec;
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);
    vec.push_back(4);
    vec.push_back(5);

    return vec;
}


int main()
{
    std::vector<int> numbers = fillMeWithNumbers();

    for (std::vector<int>::iterator iterator = numbers.begin(); numbers.end() != iterator; ++iterator)
    {
        lambda((std::vector<int>::iterator out, int i)
        {
            std::cout << *out << i;
            return *out;
        }, 1);
        exp1(iterator, 5);
    }

    return 0;
}

To avoid confusion this time I also added the fillMeWithNumbers() function to ensure it's just copy and paste. I also added another parameter to the function which is meant to show that multiple parameters aren't a problem.

But at this point I will just repeat it, I wouldn't recommend this for production code and never said it's suited for such. It's more of a gimmic.

\$\endgroup\$
  • \$\begingroup\$ Sounds like you need a more modern IDE. \$\endgroup\$ – Qix Apr 25 '17 at 9:19
  • \$\begingroup\$ by now I have... that's why I said it's obsolete now. But lisences for commercial use are expensive. \$\endgroup\$ – Mango Apr 25 '17 at 9:21
  • \$\begingroup\$ That's why you use open source ;) \$\endgroup\$ – Qix Apr 25 '17 at 9:22
  • \$\begingroup\$ yes, but on windows vs is nearly the best ide you get, even more ifyou are dealing with unreal engine, unity and co \$\endgroup\$ – Mango Apr 25 '17 at 9:23
  • \$\begingroup\$ VS supports most if not all of C++11, though, including lambdas IIRC. \$\endgroup\$ – cHao Apr 25 '17 at 13:40
1
\$\begingroup\$

Will this is silly:

#define public_functions  public
#define public_member     public
#define public_classes    public
#define private_functions private
#define private_member    private
#define private_classes   public
  • Does not give you an more real extra information
  • Break Syntax Highlighting
  • Makes anybody reading the code go uhhhh
  • Is not enforced by the language.
  • The last one is public why why why

.

#define private_classes   public   // WHY WHY WHY

Exceptions

    class bad_cast
    {
    public_functions:
        bad_cast()
            : m_Msg("bad_cast: invalid conversion of a Whatever object")
        {}

        const char* what()
        {
            return m_Msg;
        }

    private_member:
        const char* m_Msg;
    };

There is already a std::bad_cast why re--invent it.

Exceptions should probably inherit from std::runtime_error if you are going to write them (that way you can use there version of what() and storage).

Talking of what() it should definitely by marked cost. And under no circumstances should it throw. So the actual declaration you want is:

        const char* what() const throws (); // or noexcept on modern compiler

class Whatever

The copy constructor

    Whatever& operator = (const Whatever& what)

Does not provide the strong exception guarantee. Learn to use the copy and swap idiom it will save you.

Stop using Yoda Conditionals

        if (nullptr != m_WrappedValue)

This technique is like 20 years old. It was not popular then it is way out dated now. It adds a cognitive burden to the reader that is not necessary. It provides no actual benefits. What you are trying to achieve (detection of accidental assignment) can be done by the compiler already.

This is useless trick

The same affect can be achieved by just declaring a class.

    lambda((std::vector<int>::iterator out)
    {
        std::cout << *out; 
        return *out; 
    }, 1);

    // The equivalent without the overhead of any new operations.
    struct exp1 {
       int operator()(std::vector<int>::iterator out) const {
            std::cout << *out;
            return *out;
       }
    };

Also this does not have the disadvantage of a macro. Which is a really confusing message if you add a comma anywhere in the code block.

It does not compile:

bd.cpp:183:16: note: to match this '('
        lambda((std::vector<int>::iterator out)
               ^
bd.cpp:185:27: error: use of undeclared identifier 'out'; did you mean 'oct'?
            std::cout << *out;
                          ^~~
                          oct
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/ios:956:1: note:
      'oct' declared here
oct(ios_base& __str)
^
bd.cpp:185:30: error: expected '}'
            std::cout << *out;
                             ^
bd.cpp:184:9: note: to match this '{'
        {
        ^
bd.cpp:188:9: error: use of undeclared identifier 'exp1'
        exp1(iterator);
\$\endgroup\$
  • \$\begingroup\$ so where to start, I were used to do this public_function thing to get things cleaner (and I came to a similar conclusion like you did, I don't need it) but well, this code is a bit old, so sorry for that, second of all, I did not know that there is a std::bad_cast, again something learned, and the last thing I am going to address, the "yoda Conditionals" saved me a lot of trouble so they are useful for me at least. Thank you for pointing out the swapped public private sing, I never recognized that and well, I liked lambda(()){} more than writing a class , that's why i implemented it once. \$\endgroup\$ – Mango Apr 25 '17 at 15:49
  • 2
    \$\begingroup\$ Yoda conditionals don't help you in any way. There have been large articles written on the subject. Read this: softwareengineering.stackexchange.com/q/162256/12917 \$\endgroup\$ – Martin York Apr 25 '17 at 15:53
  • \$\begingroup\$ using your lambda macros just causes issues. 1) does not save you significant typing. 2) Adds lots of extra complexity 3) Is very inefficient due to all the copying and calls to new. 4) Will not work on standards compliant compilers. 5) Compiler error messages are non trivial to understand. \$\endgroup\$ – Martin York Apr 25 '17 at 15:56
  • \$\begingroup\$ Okay watch, I have no clue what you are doing with your compiler or which you are using so I simply can't tell you why it isn't working on your IDE. codepad.remoteinterview is using gcc as far as I know and it had (besides the public_function) no issue compiling it, as well as vs2010, vs2013 and vs2015. I had little to no issues with the macro, and yes, the performance suffers. \$\endgroup\$ – Mango Apr 25 '17 at 16:00
  • 1
    \$\begingroup\$ I have no idea why it is not compiling either. But the code you have supplied does not compile as is. You should try cutting and pasting the code you have supplied in the above answer into codepad . Actually let me do it for you: codepad.remoteinterview.io/YXUIBJJNNM Note I was being generous in my review above as I fixed a couple of things before reporting the errors. \$\endgroup\$ – Martin York Apr 25 '17 at 17:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.