Offer Either::left_opt() and Either::right_opt() and add some test cases

This commit is contained in:
Sebastian Messmer 2015-06-26 01:07:46 +02:00
parent e1c4327338
commit d5dadc74e7
2 changed files with 93 additions and 9 deletions

View File

@ -2,7 +2,7 @@
#ifndef MESSMER_CPP_UTILS_EITHER_H #ifndef MESSMER_CPP_UTILS_EITHER_H
#define MESSMER_CPP_UTILS_EITHER_H #define MESSMER_CPP_UTILS_EITHER_H
#include <type_traits> #include <boost/optional.hpp>
namespace cpputils { namespace cpputils {
@ -65,21 +65,50 @@ namespace cpputils {
return _side == Side::right; return _side == Side::right;
} }
//TODO Also offer a safe version of getting left/right (exceptions? nullptr?)
const Left &left() const { const Left &left() const {
return _left; return _left;
} }
Left &left() {
return const_cast<Left&>(const_cast<const Either<Left, Right>*>(this)->left());
}
const Right &right() const { const Right &right() const {
return _right; return _right;
} }
Left &left() {
return const_cast<Left&>(const_cast<const Either<Left, Right>*>(this)->left());
}
Right &right() { Right &right() {
return const_cast<Right&>(const_cast<const Either<Left, Right>*>(this)->right()); return const_cast<Right&>(const_cast<const Either<Left, Right>*>(this)->right());
} }
boost::optional<const Left&> left_opt() const {
if (_side == Side::left) {
return _left;
} else {
return boost::none;
}
}
boost::optional<Left&> left_opt() {
if (_side == Side::left) {
return _left;
} else {
return boost::none;
}
}
boost::optional<const Right&> right_opt() const {
if (_side == Side::right) {
return _right;
} else {
return boost::none;
}
}
boost::optional<Right&> right_opt() {
if (_side == Side::right) {
return _right;
} else {
return boost::none;
}
}
private: private:
union { union {
Left _left; Left _left;

View File

@ -1,8 +1,16 @@
#include <google/gtest/gtest.h> #include <google/gtest/gtest.h>
#include <google/gmock/gmock.h> #include <google/gmock/gmock.h>
#include <boost/optional/optional_io.hpp>
#include "../either.h" #include "../either.h"
#include "../macros.h" #include "../macros.h"
//TODO Go through all test cases and think about whether it makes sense to add the same test case but with primitive types.
using std::string;
using std::vector;
using namespace cpputils;
using ::testing::Test;
class OnlyMoveable { class OnlyMoveable {
public: public:
OnlyMoveable(int value_): value(value_) {} OnlyMoveable(int value_): value(value_) {}
@ -12,9 +20,11 @@ private:
DISALLOW_COPY_AND_ASSIGN(OnlyMoveable); DISALLOW_COPY_AND_ASSIGN(OnlyMoveable);
}; };
using std::string; template<typename T>
using namespace cpputils; struct StoreWith1ByteFlag {
using ::testing::Test; T val;
char flag;
};
class EitherTest: public Test { class EitherTest: public Test {
public: public:
@ -32,18 +42,39 @@ public:
void EXPECT_LEFT_IS(const Expected &expected, Either<Left, Right> &value) { void EXPECT_LEFT_IS(const Expected &expected, Either<Left, Right> &value) {
EXPECT_IS_LEFT(value); EXPECT_IS_LEFT(value);
EXPECT_EQ(expected, value.left()); EXPECT_EQ(expected, value.left());
EXPECT_EQ(expected, value.left_opt().get());
EXPECT_EQ(boost::none, value.right_opt());
const Either<Left, Right> &const_value = value; const Either<Left, Right> &const_value = value;
EXPECT_EQ(expected, const_value.left()); EXPECT_EQ(expected, const_value.left());
EXPECT_EQ(expected, const_value.left_opt().get());
EXPECT_EQ(boost::none, const_value.right_opt());
} }
template<class Left, class Right, class Expected> template<class Left, class Right, class Expected>
void EXPECT_RIGHT_IS(const Expected &expected, Either<Left, Right> &value) { void EXPECT_RIGHT_IS(const Expected &expected, Either<Left, Right> &value) {
EXPECT_IS_RIGHT(value); EXPECT_IS_RIGHT(value);
EXPECT_EQ(expected, value.right()); EXPECT_EQ(expected, value.right());
EXPECT_EQ(expected, value.right_opt().get());
EXPECT_EQ(boost::none, value.left_opt());
const Either<Left, Right> &const_value = value; const Either<Left, Right> &const_value = value;
EXPECT_EQ(expected, const_value.right()); EXPECT_EQ(expected, const_value.right());
EXPECT_EQ(expected, const_value.right_opt().get());
EXPECT_EQ(boost::none, const_value.left_opt());
} }
}; };
template<typename Left, typename Right>
void TestSpaceUsage() {
EXPECT_EQ(std::max(sizeof(StoreWith1ByteFlag<Left>), sizeof(StoreWith1ByteFlag<Right>)), sizeof(Either<Left, Right>));
}
TEST_F(EitherTest, SpaceUsage) {
TestSpaceUsage<char, int>();
TestSpaceUsage<int, short>();
TestSpaceUsage<char, short>();
TestSpaceUsage<int, string>();
TestSpaceUsage<string, vector<string>>();
}
TEST_F(EitherTest, LeftCanBeConstructed) { TEST_F(EitherTest, LeftCanBeConstructed) {
Either<int, string> val = 3; Either<int, string> val = 3;
UNUSED(val); UNUSED(val);
@ -142,6 +173,30 @@ TEST_F(EitherTest, RightCanBeMoved) {
EXPECT_EQ(5, val2.right().value); EXPECT_EQ(5, val2.right().value);
} }
TEST_F(EitherTest, ModifyLeft) {
Either<string, int> val = string("mystring1");
val.left() = "mystring2";
EXPECT_LEFT_IS("mystring2", val);
}
TEST_F(EitherTest, ModifyRight) {
Either<int, string> val = string("mystring1");
val.right() = "mystring2";
EXPECT_RIGHT_IS("mystring2", val);
}
TEST_F(EitherTest, ModifyLeftOpt) {
Either<string, int> val = string("mystring1");
val.left_opt().get() = "mystring2";
EXPECT_LEFT_IS("mystring2", val);
}
TEST_F(EitherTest, ModifyRightOpt) {
Either<int, string> val = string("mystring1");
val.right_opt().get() = "mystring2";
EXPECT_RIGHT_IS("mystring2", val);
}
TEST_F(EitherTest, LeftEquals) { TEST_F(EitherTest, LeftEquals) {
Either<string, int> val1 = string("mystring"); Either<string, int> val1 = string("mystring");
Either<string, int> val2 = string("mystring"); Either<string, int> val2 = string("mystring");