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.
(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.
-
\$\begingroup\$ Sounds like you need a more modern IDE. \$\endgroup\$Qix - MONICA WAS MISTREATED– Qix - MONICA WAS MISTREATED2017年04月25日 09:19:57 +00:00Commented Apr 25, 2017 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– Mango2017年04月25日 09:21:43 +00:00Commented Apr 25, 2017 at 9:21
-
\$\begingroup\$ That's why you use open source ;) \$\endgroup\$Qix - MONICA WAS MISTREATED– Qix - MONICA WAS MISTREATED2017年04月25日 09:22:07 +00:00Commented Apr 25, 2017 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– Mango2017年04月25日 09:23:21 +00:00Commented Apr 25, 2017 at 9:23
-
\$\begingroup\$ VS supports most if not all of C++11, though, including lambdas IIRC. \$\endgroup\$cHao– cHao2017年04月25日 13:40:29 +00:00Commented Apr 25, 2017 at 13:40
1 Answer 1
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);
-
\$\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– Mango2017年04月25日 15:49:41 +00:00Commented Apr 25, 2017 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\$Loki Astari– Loki Astari2017年04月25日 15:53:29 +00:00Commented Apr 25, 2017 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 tonew
. 4) Will not work on standards compliant compilers. 5) Compiler error messages are non trivial to understand. \$\endgroup\$Loki Astari– Loki Astari2017年04月25日 15:56:21 +00:00Commented Apr 25, 2017 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– Mango2017年04月25日 16:00:13 +00:00Commented Apr 25, 2017 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\$Loki Astari– Loki Astari2017年04月25日 17:05:56 +00:00Commented Apr 25, 2017 at 17:05