Created
November 3, 2011 21:26
-
-
Save mpusz/1337823 to your computer and use it in GitHub Desktop.
Simple TTCN structured types implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*.user | |
build/* |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _BASIC_H_ | |
#define _BASIC_H_ | |
#include <limits> | |
#include <cmath> | |
#include <memory> | |
#include <stdexcept> | |
#include <iostream> | |
class CType; | |
struct TOmit { }; | |
constexpr TOmit Omit { }; | |
class CValue { | |
bool _omit; | |
public: | |
// CValue() = default; | |
// CValue(const CValue &) { std::cout << "## copy-constructor ##" << std::endl; } | |
// CValue(CValue &&) { std::cout << "## move-constructor ##" << std::endl; } | |
// CValue &operator=(const CValue &) { std::cout << "## op= copy ##" << std::endl; return *this; } | |
// CValue &operator=(CValue &&) { std::cout << "## op= move ##" << std::endl; return *this; } | |
// void swap(CValue &other) { std::cout << "## swap ##" << std::endl; } | |
CValue() noexcept : _omit{false} {} | |
CValue(TOmit) noexcept : _omit{true} {} | |
CValue(const CValue &) = default; | |
CValue(CValue &&) = default; | |
virtual ~CValue() {} | |
CValue &operator=(TOmit) | |
{ | |
_omit = true; | |
return *this; | |
} | |
CValue &operator=(const CValue &) = default; | |
CValue &operator=(CValue &&) = default; | |
friend void swap(CValue &first, CValue &second) noexcept | |
{ | |
using std::swap; | |
swap(first._omit, second._omit); | |
} | |
virtual CValue *clone() const = 0; | |
virtual const CType &Type() const = 0; | |
virtual std::string String() const = 0; | |
virtual bool Defined() const = 0; | |
bool Omit() const { return _omit; } | |
}; | |
class CType { | |
public: | |
virtual ~CType() {} | |
virtual CType *clone() const = 0; | |
// virtual CValue *newInstance() const = 0; | |
}; | |
class CInteger; | |
class CTypeInteger : public CType { | |
static std::shared_ptr<CTypeInteger> _instance; | |
public: | |
typedef CInteger CValueType; | |
static const std::shared_ptr<CTypeInteger> &Instance() { return _instance; } | |
CTypeInteger *clone() const override { return new CTypeInteger(*this); } | |
// virtual CValueType *NewInstance() const { return new CValue; } | |
virtual bool IsValid(const CValueType &value) const { return true; } | |
}; | |
std::shared_ptr<CTypeInteger> CTypeInteger::_instance(std::make_shared<CTypeInteger>()); | |
class CInteger : public CValue { | |
public: | |
typedef int CUnderlyingType; | |
private: | |
bool _defined = false; | |
CUnderlyingType _value = 0; | |
const std::shared_ptr<const CTypeInteger> _type; | |
public: | |
static CUnderlyingType Infinity() { return std::numeric_limits<CUnderlyingType>::max(); } | |
static CUnderlyingType NInfinity() { return std::numeric_limits<CUnderlyingType>::min(); } | |
CInteger(const std::shared_ptr<const CTypeInteger> &type = CTypeInteger::Instance()): | |
_type(type) | |
{ | |
} | |
CInteger(CUnderlyingType value, const std::shared_ptr<const CTypeInteger> &type = CTypeInteger::Instance()): | |
_defined(true), _value(value), _type(type) | |
{ | |
if(!_type->IsValid(*this)) | |
throw std::logic_error("Integer value '" + String() + "' does not meet subtyping"); | |
} | |
CInteger(TOmit, const std::shared_ptr<const CTypeInteger> &type = CTypeInteger::Instance()): | |
CValue(::Omit), _type(type) | |
{ | |
} | |
CInteger(const CInteger &) = default; | |
CInteger(CInteger &&other): | |
_type(std::move(other._type)) | |
{ | |
swap(*this, other); | |
} | |
~CInteger() = default; | |
CInteger &operator=(CInteger other) | |
{ | |
if(!other.Defined()) | |
throw std::logic_error("Cannot assign not defined value"); | |
if(!_type->IsValid(other)) | |
throw std::logic_error("Integer value '" + other.String() + "' does not meet subtyping"); | |
swap(*this, other); | |
return *this; | |
} | |
CInteger &operator=(TOmit) | |
{ | |
CValue::operator=(::Omit); | |
return *this; | |
} | |
friend void swap(CInteger &first, CInteger &second) noexcept | |
{ | |
using std::swap; | |
swap(first._defined, second._defined); | |
swap(first._value, second._value); | |
// swap(first._type, second._type); | |
swap(static_cast<CValue &>(first), second); | |
} | |
CInteger *clone() const override { return new CInteger(*this); } | |
const CTypeInteger &Type() const override { return *_type; } | |
std::string String() const override { return std::to_string(_value); } | |
bool Defined() const override { return _defined || Omit(); } | |
friend CInteger operator+(const CInteger &left, const CInteger &right) | |
{ | |
if(!left._defined || !right._defined) | |
throw std::logic_error("Cannot add undefined values"); | |
return CInteger(left._value + right._value); | |
} | |
friend CInteger operator-(const CInteger &left, const CInteger &right) | |
{ | |
if(!left._defined || !right._defined) | |
throw std::logic_error("Cannot subtract undefined values"); | |
return CInteger(left._value - right._value); | |
} | |
friend bool operator==(const CInteger &left, const CInteger &right) | |
{ | |
if(left.Omit() || right.Omit()) | |
return left.Omit() == right.Omit(); | |
else if(!left._defined || !right._defined) | |
throw std::logic_error("Cannot compare undefined values"); | |
else | |
return left._value == right._value; | |
} | |
friend bool operator<(const CInteger &left, const CInteger &right) | |
{ | |
if(!left._defined || !right._defined) | |
throw std::logic_error("Cannot compare undefined values"); | |
return left._value < right._value; | |
} | |
friend bool operator>(const CInteger &left, const CInteger &right) | |
{ | |
if(!left._defined || !right._defined) | |
throw std::logic_error("Cannot compare undefined values"); | |
return left._value > right._value; | |
} | |
friend std::ostream &operator<<(std::ostream &stream, const CInteger &value) | |
{ | |
if(value._defined) | |
return stream << value._value; | |
else if(value.Omit()) | |
return stream << "omit"; | |
else | |
return stream << "<undefined>"; | |
} | |
}; | |
class CFloat; | |
class CTypeFloat : public CType { | |
static std::shared_ptr<CTypeFloat> _instance; | |
public: | |
typedef CFloat CValueType; | |
static const std::shared_ptr<CTypeFloat> &Instance() { return _instance; } | |
CTypeFloat *clone() const override { return new CTypeFloat(*this); } | |
// CValueType *NewInstance() const override { return new CValue; } | |
virtual bool IsValid(const CValueType &value) const { return true; } | |
}; | |
std::shared_ptr<CTypeFloat> CTypeFloat::_instance(std::make_shared<CTypeFloat>()); | |
class CFloat : public CValue { | |
public: | |
typedef float CUnderlyingType; | |
private: | |
bool _defined = false; | |
CUnderlyingType _value = 0; | |
const std::shared_ptr<const CTypeFloat> _type; | |
public: | |
static CUnderlyingType NaN() { return std::numeric_limits<CUnderlyingType>::quiet_NaN(); } | |
static CUnderlyingType Infinity() { return std::numeric_limits<CUnderlyingType>::infinity(); } | |
static CUnderlyingType NInfinity() { return -std::numeric_limits<CUnderlyingType>::infinity(); } | |
CFloat(const std::shared_ptr<const CTypeFloat> &type = CTypeFloat::Instance()): | |
_type(type) | |
{ | |
} | |
CFloat(CUnderlyingType value, const std::shared_ptr<const CTypeFloat> &type = CTypeFloat::Instance()): | |
_defined(true), _value(value), _type(type) | |
{ | |
if(!_type->IsValid(*this)) | |
throw std::logic_error("Float value '" + String() + "' does not meet subtyping"); | |
} | |
CFloat(TOmit, const std::shared_ptr<const CTypeFloat> &type = CTypeFloat::Instance()): | |
CValue(::Omit), _type(type) | |
{ | |
} | |
CFloat(const CFloat &) = default; | |
CFloat(CFloat &&other): | |
_type(std::move(other._type)) | |
{ | |
swap(*this, other); | |
} | |
~CFloat() = default; | |
CFloat &operator=(CFloat other) | |
{ | |
if(!other.Defined()) | |
throw std::logic_error("Cannot assign not defined value"); | |
if(!_type->IsValid(other)) | |
throw std::logic_error("Float value '" + other.String() + "' does not meet subtyping"); | |
swap(*this, other); | |
return *this; | |
} | |
CFloat &operator=(TOmit) | |
{ | |
CValue::operator=(::Omit); | |
return *this; | |
} | |
friend void swap(CFloat &first, CFloat &second) noexcept | |
{ | |
using std::swap; | |
swap(first._defined, second._defined); | |
swap(first._value, second._value); | |
// swap(first._type, second._type); | |
swap(static_cast<CValue &>(first), second); | |
} | |
CFloat *clone() const override { return new CFloat(*this); } | |
const CTypeFloat &Type() const override { return *_type; } | |
std::string String() const override { return std::to_string(_value); } | |
bool Defined() const override { return _defined || Omit(); } | |
friend CFloat operator+(const CFloat &left, const CFloat &right) | |
{ | |
if(!left._defined || !right._defined) | |
throw std::logic_error("Cannot add undefined values"); | |
return CFloat(left._value + right._value); | |
} | |
friend CFloat operator-(const CFloat &left, const CFloat &right) | |
{ | |
if(!left._defined || !right._defined) | |
throw std::logic_error("Cannot subtract undefined values"); | |
return CFloat(left._value - right._value); | |
} | |
friend bool operator==(const CFloat &left, const CFloat &right) | |
{ | |
if(left.Omit() || right.Omit()) | |
return left.Omit() == right.Omit(); | |
else if(!left._defined || !right._defined) | |
throw std::logic_error("Cannot compare undefined values"); | |
else { | |
bool lnan = std::isnan(left._value); | |
bool rnan = std::isnan(right._value); | |
if(!lnan && !rnan) | |
return left._value == right._value; | |
return lnan && rnan; | |
} | |
} | |
friend bool operator<(const CFloat &left, const CFloat &right) | |
{ | |
if(!left._defined || !right._defined) | |
throw std::logic_error("Cannot compare undefined values"); | |
bool lnan = std::isnan(left._value); | |
bool rnan = std::isnan(right._value); | |
if(!lnan && !rnan) | |
return left._value < right._value; | |
return !lnan; | |
} | |
friend bool operator>(const CFloat &left, const CFloat &right) | |
{ | |
if(!left._defined || !right._defined) | |
throw std::logic_error("Cannot compare undefined values"); | |
bool lnan = std::isnan(left._value); | |
bool rnan = std::isnan(right._value); | |
if(!lnan && !rnan) | |
return left._value > right._value; | |
return !rnan; | |
} | |
friend std::ostream &operator<<(std::ostream &stream, const CFloat &value) | |
{ | |
if(value._defined) | |
return stream << value._value; | |
else if(value.Omit()) | |
return stream << "omit"; | |
else | |
return stream << "<undefined>"; | |
} | |
}; | |
class CString; | |
class CTypeString : public CType { | |
static std::shared_ptr<CTypeString> _instance; | |
public: | |
typedef CString CValueType; | |
static const std::shared_ptr<CTypeString> &Instance() { return _instance; } | |
CTypeString *clone() const override { return new CTypeString(*this); } | |
// CValueType *NewInstance() const override { return new CValue; } | |
virtual bool IsValid(const CValueType &value) const { return true; } | |
}; | |
std::shared_ptr<CTypeString> CTypeString::_instance(std::make_shared<CTypeString>()); | |
class CString : public CValue { | |
public: | |
typedef std::string CUnderlyingType; | |
private: | |
bool _defined = false; | |
CUnderlyingType _value = ""; | |
const std::shared_ptr<const CTypeString> _type; | |
public: | |
CString(const std::shared_ptr<const CTypeString> &type = CTypeString::Instance()): | |
_type(type) | |
{ | |
} | |
template<typename S> | |
CString(S &&value, const std::shared_ptr<const CTypeString> &type = CTypeString::Instance()): | |
_defined(true), _value(std::forward<S>(value)), _type(type) | |
{ | |
if(!_type->IsValid(*this)) | |
throw std::logic_error("String value '" + String() + "' does not meet subtyping"); | |
} | |
CString(TOmit, const std::shared_ptr<const CTypeString> &type = CTypeString::Instance()): | |
CValue(::Omit), _type(type) | |
{ | |
} | |
CString(const CString &) = default; | |
CString(CString &&other): | |
_type(std::move(other._type)) | |
{ | |
swap(*this, other); | |
} | |
~CString() = default; | |
CString &operator=(CString other) | |
{ | |
if(!other.Defined()) | |
throw std::logic_error("Cannot assign not defined value"); | |
if(!_type->IsValid(other)) | |
throw std::logic_error("String value '" + other.String() + "' does not meet subtyping"); | |
swap(*this, other); | |
return *this; | |
} | |
CString &operator=(TOmit) | |
{ | |
CValue::operator=(::Omit); | |
return *this; | |
} | |
friend void swap(CString &first, CString &second) noexcept | |
{ | |
using std::swap; | |
swap(first._defined, second._defined); | |
swap(first._value, second._value); | |
// swap(first._type, second._type); | |
swap(static_cast<CValue &>(first), second); | |
} | |
CString *clone() const override { return new CString(*this); } | |
const CTypeString &Type() const override { return *_type; } | |
std::string String() const override { return _value; } | |
bool Defined() const override { return _defined || Omit(); } | |
friend bool operator==(const CString &left, const CString &right) | |
{ | |
if(left.Omit() || right.Omit()) | |
return left.Omit() == right.Omit(); | |
else if(!left._defined || !right._defined) | |
throw std::logic_error("Cannot compare undefined values"); | |
else | |
return left._value == right._value; | |
} | |
friend std::ostream &operator<<(std::ostream &stream, const CString &value) | |
{ | |
if(value._defined) | |
return stream << value._value; | |
else if(value.Omit()) | |
return stream << "omit"; | |
else | |
return stream << "<undefined>"; | |
} | |
}; | |
#endif /* _BASIC_H_ */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
project(ttcnSimple_types) | |
cmake_minimum_required(VERSION 2.8) | |
add_definitions(-std=c++11 -Wall) | |
find_package(Boost COMPONENTS unit_test_framework REQUIRED) | |
include_directories(${Boost_INCLUDE_DIRS}) | |
add_executable(${PROJECT_NAME} | |
main.cpp | |
) | |
target_link_libraries(${PROJECT_NAME} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) | |
set(UNIT_TEST_CMD ${PROJECT_NAME} | sed -r 's/^\([^\)]+\)\\\(\([0-9]+\)\\\)/\\1:\\2/' 1>&2) | |
add_custom_target(test ALL ${UNIT_TEST_CMD} | |
WORKING_DIRECTORY ${PROJECT_BINARY_DIR} | |
COMMENT "Running Unit Tests...") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "record.h" | |
//#include "union.h" | |
#include "subtype.h" | |
#include <iostream> | |
#define BOOST_TEST_DYN_LINK | |
#define BOOST_TEST_MODULE ttcn3 | |
#include <boost/test/unit_test.hpp> | |
BOOST_AUTO_TEST_SUITE( basic_types ) | |
BOOST_AUTO_TEST_CASE( core_6_1_2_1_Ex ) | |
{ | |
std::shared_ptr<CTypeFloatOne> pi(std::make_shared<CTypeFloatOne>(3.1415926)); | |
BOOST_CHECK_EXCEPTION( CFloat test(2, pi), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Float value '2.000000' does not meet subtyping"; } ); | |
CFloat test(3.1415926, pi); | |
CFloat test1 = 1.0 + test; | |
BOOST_CHECK_EXCEPTION( test = test1, | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == "Float value '4.141593' does not meet subtyping"; } ); | |
BOOST_CHECK_NO_THROW( test = test1 - 1 ); | |
BOOST_CHECK_EQUAL( test, 3.1415926 ); | |
CFloat test2(pi); | |
BOOST_CHECK_EXCEPTION( test1 = test2, | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == "Cannot assign not defined value"; } ); | |
BOOST_CHECK_EXCEPTION( test2 = 3.14, | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == "Float value '3.140000' does not meet subtyping"; } ); | |
BOOST_CHECK_NO_THROW( test2 = 3.1415926 ); | |
BOOST_CHECK_NO_THROW( test1 = test2 ); | |
BOOST_CHECK_EQUAL( test1, 3.1415926 ); | |
} | |
BOOST_AUTO_TEST_CASE( core_6_1_2_2_Ex ) | |
{ | |
std::shared_ptr<CTypeIntegerRange> part1(std::make_shared<CTypeIntegerRange>(0, 5, true)); | |
std::shared_ptr<CTypeIntegerList> part2(std::make_shared<CTypeIntegerList>(CTypeIntegerList({ | |
std::make_shared<CTypeIntegerOne>(5), | |
std::make_shared<CTypeIntegerOne>(7), | |
std::make_shared<CTypeIntegerOne>(9) }))); | |
std::shared_ptr<CTypeIntegerList> all(std::make_shared<CTypeIntegerList>(CTypeIntegerList({ part1, part2 }))); | |
BOOST_CHECK_NO_THROW( CInteger test(1, all) ); | |
BOOST_CHECK_NO_THROW( CInteger test(4, all) ); | |
BOOST_CHECK_NO_THROW( CInteger test(5, all) ); | |
BOOST_CHECK_NO_THROW( CInteger test(7, all) ); | |
BOOST_CHECK_NO_THROW( CInteger test(9, all) ); | |
BOOST_CHECK_EXCEPTION( CInteger test(-1, all), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '-1' does not meet subtyping"; } ); | |
BOOST_CHECK_EXCEPTION( CInteger test(0, all), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '0' does not meet subtyping"; } ); | |
BOOST_CHECK_EXCEPTION( CInteger test(6, all), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '6' does not meet subtyping"; } ); | |
BOOST_CHECK_EXCEPTION( CInteger test(10, all), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '10' does not meet subtyping"; } ); | |
} | |
BOOST_AUTO_TEST_CASE( core_6_1_2_3_Ex_1 ) | |
{ | |
std::shared_ptr<CTypeIntegerRange> range1(std::make_shared<CTypeIntegerRange>(CInteger::NInfinity(), -1, true)); | |
BOOST_CHECK_EXCEPTION( CInteger test1(CInteger::NInfinity(), range1), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '" + CInteger(CInteger::NInfinity()).String() + "' does not meet subtyping"; } ); | |
BOOST_CHECK_NO_THROW( CInteger test1(CInteger::NInfinity() + 1, range1) ); | |
std::shared_ptr<CTypeIntegerRange> range2(std::make_shared<CTypeIntegerRange>(0, 255)); | |
std::shared_ptr<CTypeIntegerRange> range3(std::make_shared<CTypeIntegerRange>(0, 256, false, true)); | |
std::shared_ptr<CTypeIntegerRange> range4(std::make_shared<CTypeIntegerRange>(-1, 256, true, true)); | |
BOOST_CHECK_NO_THROW( CInteger test(0, range2) ); | |
BOOST_CHECK_NO_THROW( CInteger test(255, range2) ); | |
BOOST_CHECK_EXCEPTION( CInteger test(-1, range2), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '-1' does not meet subtyping"; } ); | |
BOOST_CHECK_EXCEPTION( CInteger test1(256, range2), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '256' does not meet subtyping"; } ); | |
BOOST_CHECK_NO_THROW( CInteger test(0, range3) ); | |
BOOST_CHECK_NO_THROW( CInteger test(255, range3) ); | |
BOOST_CHECK_EXCEPTION( CInteger test(-1, range3), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '-1' does not meet subtyping"; } ); | |
BOOST_CHECK_EXCEPTION( CInteger test1(256, range3), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '256' does not meet subtyping"; } ); | |
BOOST_CHECK_NO_THROW( CInteger test(0, range4) ); | |
BOOST_CHECK_NO_THROW( CInteger test(255, range4) ); | |
BOOST_CHECK_EXCEPTION( CInteger test(-1, range4), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '-1' does not meet subtyping"; } ); | |
BOOST_CHECK_EXCEPTION( CInteger test1(256, range4), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '256' does not meet subtyping"; } ); | |
std::shared_ptr<CTypeFloatRange> piRange(std::make_shared<CTypeFloatRange>(3.14, 3142E-3)); | |
CFloat test(3.14, piRange); | |
BOOST_CHECK_NO_THROW( test = test + 0.001 ); | |
BOOST_CHECK_NO_THROW( test = test + 0.001 ); | |
BOOST_CHECK_EXCEPTION( test = test + 0.001, | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Float value '3.143000' does not meet subtyping"; } ); | |
std::shared_ptr<CTypeFloatRange> LessThanPi(std::make_shared<CTypeFloatRange>(CFloat::NInfinity(), 3142E-3)); | |
std::shared_ptr<CTypeFloatRange> Numbers(std::make_shared<CTypeFloatRange>(CFloat::NInfinity(), CFloat::Infinity())); | |
BOOST_CHECK_NO_THROW( CFloat test2(0, Numbers) ); | |
BOOST_CHECK_NO_THROW( CFloat test2(CFloat::NInfinity(), Numbers) ); | |
BOOST_CHECK_NO_THROW( CFloat test2(CFloat::Infinity(), Numbers) ); | |
BOOST_CHECK_EXCEPTION( CFloat test2(CFloat::NaN(), Numbers), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Float value 'nan' does not meet subtyping"; } ); | |
BOOST_CHECK_THROW( CTypeFloatRange Wrong(CFloat::NInfinity(), CFloat::NaN()), | |
std::logic_error ); | |
} | |
BOOST_AUTO_TEST_CASE( core_6_1_2_6_1_Ex_1 ) | |
{ | |
std::shared_ptr<CTypeIntegerList> MyIntegerRange(std::make_shared<CTypeIntegerList>(CTypeIntegerList({ | |
std::make_shared<CTypeIntegerOne>(1), | |
std::make_shared<CTypeIntegerOne>(2), | |
std::make_shared<CTypeIntegerOne>(3), | |
std::make_shared<CTypeIntegerRange>(10, 20, false, true), | |
std::make_shared<CTypeIntegerOne>(99), | |
std::make_shared<CTypeIntegerOne>(100) }))); | |
BOOST_CHECK_NO_THROW( CInteger test(1, MyIntegerRange) ); | |
BOOST_CHECK_NO_THROW( CInteger test(3, MyIntegerRange) ); | |
BOOST_CHECK_NO_THROW( CInteger test(10, MyIntegerRange) ); | |
BOOST_CHECK_NO_THROW( CInteger test(19, MyIntegerRange) ); | |
BOOST_CHECK_NO_THROW( CInteger test(99, MyIntegerRange) ); | |
BOOST_CHECK_NO_THROW( CInteger test(100, MyIntegerRange) ); | |
BOOST_CHECK_EXCEPTION( CInteger test(0, MyIntegerRange), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '0' does not meet subtyping"; } ); | |
BOOST_CHECK_EXCEPTION( CInteger test(4, MyIntegerRange), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '4' does not meet subtyping"; } ); | |
BOOST_CHECK_EXCEPTION( CInteger test(20, MyIntegerRange), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '20' does not meet subtyping"; } ); | |
BOOST_CHECK_EXCEPTION( CInteger test(101, MyIntegerRange), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '101' does not meet subtyping"; } ); | |
std::shared_ptr<CTypeFloatList> lessThanPiAndNaN(std::make_shared<CTypeFloatList>(CTypeFloatList({ | |
std::make_shared<CTypeFloatRange>(CFloat::NInfinity(), 3142E-3), | |
std::make_shared<CTypeFloatOne>(CFloat::NaN()) }))); | |
BOOST_CHECK_NO_THROW( CFloat test(CFloat::NaN(), lessThanPiAndNaN) ); | |
BOOST_CHECK_EXCEPTION( CFloat test(4, lessThanPiAndNaN), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Float value '4.000000' does not meet subtyping"; } ); | |
} | |
// // is it allowed? | |
// { | |
// CTypeIntegerRange range(0, 255); | |
// // CTypeIntegerOne range1(range, -3); | |
// // CIntegerSub test_1(range1, -3); | |
// // CTypeIntegerRange range2(range, 0, 15); | |
// // CIntegerSub test_2(range2, 15); | |
// } | |
BOOST_AUTO_TEST_SUITE_END() | |
BOOST_AUTO_TEST_SUITE( struct_types ) | |
class CTypeMyRecordType : public CTypeRecord<CTypeMyRecordType, CTypeInteger, CTypeFloat, CTypeString> | |
{ | |
static std::shared_ptr<CTypeMyRecordType> _instance; | |
public: | |
static const std::shared_ptr<CTypeMyRecordType> &Instance() { return _instance; } | |
CTypeMyRecordType(): CTypeRecord(TFields({ false, "field1", CTypeInteger::Instance() }, | |
{ false, "field2", CTypeFloat::Instance() }, | |
{ false, "field3", CTypeString::Instance() } )) {} | |
}; | |
std::shared_ptr<CTypeMyRecordType> CTypeMyRecordType::_instance(std::make_shared<CTypeMyRecordType>()); | |
typedef CRecord<CTypeMyRecordType::CCurrentType> CMyRecordType; | |
BOOST_AUTO_TEST_CASE( core_6_2_Ex_1 ) | |
{ | |
CMyRecordType MyRecordValue(CMyRecordType::TValues(1, 3.14, "PI"), | |
CTypeMyRecordType::Instance()); | |
BOOST_CHECK_EQUAL( *MyRecordValue.Names()[0], "field1" ); | |
BOOST_CHECK_EQUAL( *MyRecordValue.Names()[1], "field2" ); | |
BOOST_CHECK_EQUAL( *MyRecordValue.Names()[2], "field3" ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Defined(), true ); | |
} | |
BOOST_AUTO_TEST_CASE( core_6_2_Ex_2 ) | |
{ | |
CMyRecordType MyRecordValue(CMyRecordType::TValues(1, CFloat(), "PI"), | |
CTypeMyRecordType::Instance()); | |
BOOST_CHECK_EQUAL( MyRecordValue.Defined(), false ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<1>().Defined(), false ); | |
BOOST_CHECK_NO_THROW( MyRecordValue.Field<1>(3.141) ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Defined(), true ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<0>(), 1 ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<1>(), 3.141 ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<2>(), "PI" ); | |
} | |
BOOST_AUTO_TEST_CASE( core_6_2_Ex_4 ) | |
{ | |
CMyRecordType MyVariable(CMyRecordType::TValues(1, 3.14, CString()), | |
CTypeMyRecordType::Instance()); | |
BOOST_CHECK_EQUAL( MyVariable.Defined(), false ); | |
BOOST_CHECK_EQUAL( MyVariable.Field<2>().Defined(), false ); | |
BOOST_CHECK_NO_THROW( MyVariable = CMyRecordType::TValues(2, CFloat(), CString()) ); | |
BOOST_CHECK_EQUAL( MyVariable.Defined(), false ); | |
BOOST_CHECK_EQUAL( MyVariable.Field<0>(), 2 ); | |
BOOST_CHECK_EQUAL( MyVariable.Field<1>(), 3.14 ); | |
BOOST_CHECK_EQUAL( MyVariable.Field<2>().Defined(), false ); | |
BOOST_CHECK_NO_THROW( MyVariable.Field<2>("pi") ); | |
BOOST_CHECK_EQUAL( MyVariable.Field<2>(), "pi" ); | |
BOOST_CHECK_NO_THROW( MyVariable.Field<2>() = "Pi" ); | |
BOOST_CHECK_EQUAL( MyVariable.Field<2>(), "Pi" ); | |
BOOST_CHECK_EQUAL( MyVariable.Defined(), true ); | |
} | |
class CTypeMyRecord1 : public CTypeRecord<CTypeMyRecord1, CTypeInteger, CTypeMyRecord1, CTypeFloat> | |
{ | |
static std::shared_ptr<CTypeMyRecord1> _instance; | |
public: | |
static const std::shared_ptr<CTypeMyRecord1> &Instance() { return _instance; } | |
CTypeMyRecord1(): CTypeRecord(TFields({ false, "field1", CTypeInteger::Instance() }, | |
{ true, "field2", CTypeMyRecord1::Instance() }, | |
{ false, "field3", CTypeFloat::Instance() } )) {} | |
}; | |
std::shared_ptr<CTypeMyRecord1> CTypeMyRecord1::_instance(std::make_shared<CTypeMyRecord1>()); | |
typedef CRecord<CTypeMyRecord1::CCurrentType> CMyRecord1; | |
class CTypeMyRecord2 : public CTypeRecord<CTypeMyRecord2, CTypeInteger, CTypeMyRecord2, CTypeFloat> | |
{ | |
static std::shared_ptr<CTypeMyRecord2> _instance; | |
public: | |
static std::shared_ptr<CTypeMyRecord2> &Instance() { return _instance; } | |
CTypeMyRecord2(): CTypeRecord(TFields({ false, "field1", CTypeInteger::Instance() }, | |
{ false, "field2", CTypeMyRecord2::Instance() }, | |
{ false, "field3", CTypeFloat::Instance() } )) {} | |
}; | |
std::shared_ptr<CTypeMyRecord2> CTypeMyRecord2::_instance; | |
typedef CRecord<CTypeMyRecord2::CCurrentType> CMyRecord2; | |
class CTypeMyRecord3 : public CTypeRecord<CTypeMyRecord3, CTypeInteger, CTypeMyRecord3, CTypeMyRecord3> | |
{ | |
static std::shared_ptr<CTypeMyRecord3> _instance; | |
public: | |
static std::shared_ptr<CTypeMyRecord3> &Instance() { return _instance; } | |
CTypeMyRecord3(): CTypeRecord(TFields({ false, "field1", CTypeInteger::Instance() }, | |
{ false, "field2", CTypeMyRecord3::Instance() }, | |
{ true, "field3", CTypeMyRecord3::Instance() } )) {} | |
}; | |
std::shared_ptr<CTypeMyRecord3> CTypeMyRecord3::_instance; | |
typedef CRecord<CTypeMyRecord3::CCurrentType> CMyRecord3; | |
class CTypeMyRecord4 : public CTypeRecord<CTypeMyRecord4, CTypeInteger, CTypeMyRecord4, CTypeMyRecord4> | |
{ | |
static std::shared_ptr<CTypeMyRecord4> _instance; | |
public: | |
static std::shared_ptr<CTypeMyRecord4> &Instance() { return _instance; } | |
CTypeMyRecord4(): CTypeRecord(TFields({ false, "field1", CTypeInteger::Instance() }, | |
{ true, "field2", CTypeMyRecord4::Instance() }, | |
{ false, "field3", CTypeMyRecord4::Instance() } )) {} | |
}; | |
std::shared_ptr<CTypeMyRecord4> CTypeMyRecord4::_instance; | |
typedef CRecord<CTypeMyRecord4::CCurrentType> CMyRecord4; | |
BOOST_AUTO_TEST_CASE( core_6_2_Ex_5 ) | |
{ | |
CMyRecord1 test(CMyRecord1::TValues(1, CMyRecord1(CTypeMyRecord1::Instance()), 3.14), | |
CTypeMyRecord1::Instance()); | |
BOOST_CHECK_EQUAL( test.Defined(), false ); | |
test.Field<1>(CMyRecord1(CMyRecord1::TValues(2, CMyRecord1(Omit, CTypeMyRecord1::Instance()), 3.141), | |
CTypeMyRecord1::Instance())); | |
BOOST_CHECK_EQUAL( test.Defined(), true ); | |
BOOST_CHECK_EQUAL( test.Field<0>(), 1 ); | |
BOOST_CHECK_EQUAL( test.Field<1>().Field<0>(), 2 ); | |
BOOST_CHECK_EQUAL( test.Field<1>().Field<1>().Omit(), true ); | |
BOOST_CHECK_EQUAL( test.Field<1>().Field<1>().Defined(), true ); | |
BOOST_CHECK_EQUAL( test.Field<1>().Field<2>(), 3.141 ); | |
BOOST_CHECK_EQUAL( test.Field<2>(), 3.14 ); | |
BOOST_CHECK_EXCEPTION( CTypeMyRecord2::Instance() = std::make_shared<CTypeMyRecord2>(), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Record infinite recursion detected (field 'field2' should be optional)"; } ); | |
BOOST_CHECK_EXCEPTION( CTypeMyRecord3::Instance() = std::make_shared<CTypeMyRecord3>(), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Record infinite recursion detected (field 'field2' should be optional)"; } ); | |
BOOST_CHECK_EXCEPTION( CTypeMyRecord4::Instance() = std::make_shared<CTypeMyRecord4>(), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Record infinite recursion detected (field 'field3' should be optional)"; } ); | |
} | |
BOOST_AUTO_TEST_CASE( core_6_2_Ex_6 ) | |
{ | |
} | |
// // recursive union | |
// CTypeUnion MyUnion1Type({ "choice1", "choice2" }); | |
// class CMyUnion1: public CUnion<CMyUnion1, CInteger> { | |
// public: | |
// typedef CUnionVariant<0, CMyUnion1> CVariantchoice1; | |
// CMyUnion1(): CUnion(MyUnion1Type) {} | |
// CMyUnion1(CVariantchoice1 &&variant): CUnion(MyUnion1Type, std::move(variant)) {} | |
// CMyUnion1(const CMyUnion1 &) = default; | |
// CMyUnion1(CMyUnion1 &&) = default; | |
// virtual CMyUnion1 *clone() const { return new CMyUnion1(*this); } | |
// CMyUnion1 &choice1() { return *Variant<0>(); } | |
// void choice1(const CMyUnion1 &value) { Variant<0>(value); } | |
// void choice1(CMyUnion1 &&value) { Variant<0>(std::move(value)); } | |
// CInteger &choice2() { return *Variant<1>(); } | |
// void choice2(const CInteger &value) { Variant<1>(value); } | |
// void choice2(CInteger &&value) { Variant<1>(std::move(value)); } | |
// }; | |
// void Test_6_2() | |
// { | |
// CMyRecord1 rec1; | |
// CEmptyRecord empty; | |
// } | |
class CTypeMyOtherRecordType : public CTypeRecord<CTypeMyOtherRecordType, CTypeString, CTypeInteger> | |
{ | |
static std::shared_ptr<CTypeMyOtherRecordType> _instance; | |
public: | |
static const std::shared_ptr<CTypeMyOtherRecordType> &Instance() { return _instance; } | |
CTypeMyOtherRecordType(): CTypeRecord(TFields({ false, "field1", CTypeString::Instance() }, | |
{ false, "field2", CTypeInteger::Instance() } )) {} | |
}; | |
std::shared_ptr<CTypeMyOtherRecordType> CTypeMyOtherRecordType::_instance(std::make_shared<CTypeMyOtherRecordType>()); | |
typedef CRecord<CTypeMyOtherRecordType::CCurrentType> CMyOtherRecordType; | |
class CTypeMyRecordType2 : public CTypeRecord<CTypeMyRecordType2, CTypeInteger, CTypeMyOtherRecordType, CTypeString> | |
{ | |
static std::shared_ptr<CTypeMyRecordType2> _instance; | |
public: | |
static const std::shared_ptr<CTypeMyRecordType2> &Instance() { return _instance; } | |
CTypeMyRecordType2(): CTypeRecord(TFields({ false, "field1", CTypeInteger::Instance() }, | |
{ true, "field2", CTypeMyOtherRecordType::Instance() }, | |
{ false, "field3", CTypeString::Instance() } )) {} | |
}; | |
std::shared_ptr<CTypeMyRecordType2> CTypeMyRecordType2::_instance(std::make_shared<CTypeMyRecordType2>()); | |
typedef CRecord<CTypeMyRecordType2::CCurrentType> CMyRecordType2; | |
BOOST_AUTO_TEST_CASE( core_6_2_1_Ex_1 ) | |
{ | |
CMyRecordType2 MyRecordValue(CMyRecordType2::TValues(3, | |
CMyOtherRecordType(CTypeMyOtherRecordType::Instance()), | |
"PI"), | |
CTypeMyRecordType2::Instance()); | |
BOOST_CHECK_EQUAL( MyRecordValue.Defined(), false ); | |
BOOST_CHECK_NO_THROW( MyRecordValue.Field<1>(CMyOtherRecordType(CMyOtherRecordType::TValues("pi", 1), | |
CTypeMyOtherRecordType::Instance())) ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Defined(), true ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<0>(), 3 ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<1>().Field<0>(), "pi" ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<1>().Field<1>(), 1 ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<2>(), "PI" ); | |
CMyRecordType2 MyRecordValue2(CMyRecordType2::TValues(3, | |
CMyOtherRecordType(CMyOtherRecordType::TValues("pi", 1), | |
CTypeMyOtherRecordType::Instance()), | |
"PI"), | |
CTypeMyRecordType2::Instance()); | |
BOOST_CHECK_EQUAL( MyRecordValue2.Defined(), true ); | |
BOOST_CHECK_EQUAL( MyRecordValue2.Field<0>(), 3 ); | |
BOOST_CHECK_EQUAL( MyRecordValue2.Field<1>().Field<0>(), "pi" ); | |
BOOST_CHECK_EQUAL( MyRecordValue2.Field<1>().Field<1>(), 1 ); | |
BOOST_CHECK_EQUAL( MyRecordValue2.Field<2>(), "PI" ); | |
CMyRecordType2 MyRecordValue3(CTypeMyRecordType2::Instance()); | |
BOOST_CHECK_EQUAL( MyRecordValue3.Defined(), false ); | |
MyRecordValue3 = (CMyRecordType2::TValues(3, | |
CMyOtherRecordType(CMyOtherRecordType::TValues("pi", 1), | |
CTypeMyOtherRecordType::Instance()), | |
"PI")); | |
BOOST_CHECK_EQUAL( MyRecordValue3.Defined(), true ); | |
BOOST_CHECK_EQUAL( MyRecordValue3.Field<0>(), 3 ); | |
BOOST_CHECK_EQUAL( MyRecordValue3.Field<1>().Field<0>(), "pi" ); | |
BOOST_CHECK_EQUAL( MyRecordValue3.Field<1>().Field<1>(), 1 ); | |
BOOST_CHECK_EQUAL( MyRecordValue3.Field<2>(), "PI" ); | |
} | |
class CTypeMyEmptyRecord : public CTypeRecord<CTypeMyEmptyRecord> | |
{ | |
static std::shared_ptr<CTypeMyEmptyRecord> _instance; | |
public: | |
static const std::shared_ptr<CTypeMyEmptyRecord> &Instance() { return _instance; } | |
CTypeMyEmptyRecord(): CTypeRecord(TFields()) {} | |
}; | |
std::shared_ptr<CTypeMyEmptyRecord> CTypeMyEmptyRecord::_instance(std::make_shared<CTypeMyEmptyRecord>()); | |
typedef CRecord<CTypeMyEmptyRecord::CCurrentType> CMyEmptyRecord; | |
BOOST_AUTO_TEST_CASE( core_6_2_1_Ex_2 ) | |
{ | |
CMyEmptyRecord MyEmptyRecord(CMyEmptyRecord::TValues(), | |
CTypeMyEmptyRecord::Instance()); | |
BOOST_CHECK_EQUAL( MyEmptyRecord.Defined(), true ); | |
} | |
BOOST_AUTO_TEST_CASE( core_6_2_1_Ex_3 ) | |
{ | |
CInteger MyIntegerValue(1); | |
CMyOtherRecordType MyOtherRecordType(CMyOtherRecordType::TValues("11001", 1), | |
CTypeMyOtherRecordType::Instance()); | |
CMyRecordType2 MyRecordValue(CMyRecordType2::TValues(MyIntegerValue, MyOtherRecordType, "A string"), | |
CTypeMyRecordType2::Instance()); | |
BOOST_CHECK_EQUAL( MyRecordValue.Defined(), true ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<0>(), 1 ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<1>().Field<0>(), "11001" ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<1>().Field<1>(), 1 ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<2>(), "A string" ); | |
} | |
BOOST_AUTO_TEST_CASE( core_6_2_1_Ex_4 ) | |
{ | |
CInteger MyIntegerValue(1); | |
CMyRecordType2 MyRecordValue(CMyRecordType2::TValues(MyIntegerValue, | |
CMyOtherRecordType(CMyOtherRecordType::TValues("11001", 1), | |
CTypeMyOtherRecordType::Instance()), | |
"A string"), | |
CTypeMyRecordType2::Instance()); | |
BOOST_CHECK_EQUAL( MyRecordValue.Defined(), true ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<0>(), 1 ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<1>().Field<0>(), "11001" ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<1>().Field<1>(), 1 ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<2>(), "A string" ); | |
} | |
BOOST_AUTO_TEST_CASE( core_6_2_1_2_Ex_2 ) | |
{ | |
CMyRecordType2 MyRecordValue(CMyRecordType2::TValues(1, | |
CMyOtherRecordType(Omit, CTypeMyOtherRecordType::Instance()), | |
"A string"), | |
CTypeMyRecordType2::Instance()); | |
BOOST_CHECK_EQUAL( MyRecordValue.Defined(), true ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<1>().Omit(), true ); | |
BOOST_CHECK_EQUAL( MyRecordValue.Field<1>().Defined(), true ); | |
CMyRecordType2 MyRecordValue2(CMyRecordType2::TValues(1, | |
CMyOtherRecordType(CTypeMyOtherRecordType::Instance()), | |
"A string"), | |
CTypeMyRecordType2::Instance()); | |
BOOST_CHECK_EQUAL( MyRecordValue2.Defined(), false ); | |
BOOST_CHECK_EQUAL( MyRecordValue2.Field<1>().Omit(), false ); | |
BOOST_CHECK_EQUAL( MyRecordValue2.Field<1>().Defined(), false ); | |
BOOST_CHECK_EXCEPTION( CMyRecordType2 MyRecordValue3(CMyRecordType2::TValues(1, | |
CMyOtherRecordType(Omit, CTypeMyOtherRecordType::Instance()), | |
Omit), | |
CTypeMyRecordType2::Instance()), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Cannot set 'omit' for not optional record field 'field3'"; } ); | |
CMyRecordType2 MyRecordValue3(CMyRecordType2::TValues(1, | |
CMyOtherRecordType(CTypeMyOtherRecordType::Instance()), | |
"A string"), | |
CTypeMyRecordType2::Instance()); | |
BOOST_CHECK_EXCEPTION( MyRecordValue3 = CMyRecordType2::TValues(CInteger(), | |
CMyOtherRecordType(Omit, CTypeMyOtherRecordType::Instance()), | |
Omit), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Cannot set 'omit' for not optional record field 'field3'"; } ); | |
BOOST_CHECK_NO_THROW( MyRecordValue3.Field<1>(CMyOtherRecordType(Omit, CTypeMyOtherRecordType::Instance())) ); | |
BOOST_CHECK_EXCEPTION( MyRecordValue3.Field<2>(Omit), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Cannot set 'omit' for not optional record field 'field3'"; } ); | |
} | |
// class CTypeMyNestedRecordType : public CTypeRecord<CTypeMyNestedRecordType, CTypeOuterField1, CTypeString, CTypeInteger> | |
// { | |
// public: | |
// class CTypeOuterField1 : public CTypeRecord<CTypeOuterField1, CTypeInteger, CTypeFloat> | |
// { | |
// public: | |
// static std::shared_ptr<CTypeOuterField1> instance; | |
// CTypeOuterField1(): CTypeRecord(TFields({ false, "field1", CTypeInteger::Instance() }, | |
// { false, "field2", CTypeFloat::Instance() } )) {} | |
// }; | |
// typedef CRecord<CTypeOuterField1::CCurrentType> COuterField1; | |
// static std::shared_ptr<CTypeMyOtherRecordType> instance; | |
// // CTypeMyOtherRecordType(): CTypeRecord(TFields({ false, "field1", CTypeString::Instance() }, | |
// // { false, "field2", CTypeInteger::Instance() } )) {} | |
// }; | |
// std::shared_ptr<CTypeMyOtherRecordType::CTypeOuterField1> CTypeMyOtherRecordType::CTypeOuterField1::Instance()(new CTypeMyOtherRecordType::CTypeOuterField1); | |
// std::shared_ptr<CTypeMyOtherRecordType> CTypeMyOtherRecordType::Instance()(new CTypeMyOtherRecordType); | |
// typedef CRecord<CTypeMyOtherRecordType::CCurrentType> CMyOtherRecordType; | |
BOOST_AUTO_TEST_CASE( core_6_2_1_3_Ex ) | |
{ | |
} | |
// void Test_6_2_1() | |
// { | |
// // CInteger a1(5); | |
// // record1.a1() = a1 + record1.a2(); | |
// // TestValue(record1.a1(), CInteger(7)); | |
// // try { | |
// // record1.Get("test"); | |
// // std::cout << "Exception: FAIL" << std::endl; | |
// // } | |
// // catch(std::logic_error &ex) { | |
// // std::cout << "Exception: PASS (" << ex.what() << ")" << std::endl; | |
// // } | |
// // try { | |
// // record1.Set("test", CInteger(99)); | |
// // std::cout << "Exception: FAIL" << std::endl; | |
// // } | |
// // catch(std::logic_error &ex) { | |
// // std::cout << "Exception: PASS (" << ex.what() << ")" << std::endl; | |
// // } | |
// // TestValue(dynamic_cast<const CInteger &>(record1.Get("a1")), CInteger(7)); | |
// // CInteger a(0); | |
// // CInteger a2(10); | |
// // record1.Set("a1", a2); | |
// // a = record1.a1() + record1.a2(); | |
// // TestValue(a, CInteger(12)); | |
// // CMyRecord record2; | |
// // TestNotDefined(record2); | |
// // try { | |
// // record2.a1(); | |
// // std::cout << "Exception: FAIL" << std::endl; | |
// // } | |
// // catch(std::exception &ex) { | |
// // std::cout << "Exception: PASS (" << ex.what() << ")" << std::endl; | |
// // } | |
// // try { | |
// // record2.Set("a1", CString("")); | |
// // std::cout << "Exception: FAIL" << std::endl; | |
// // } | |
// // catch(std::exception &ex) { | |
// // std::cout << "Exception: PASS (" << ex.what() << ")" << std::endl; | |
// // } | |
// // record2.a1(CInteger(1)); | |
// // TestNotDefined(record2); | |
// // record2.a2(AA(2)); | |
// // TestDefined(record2); | |
// // TestValue(record2.a1() + record2.a2(), CInteger(3)); | |
// } | |
class CTypeMyRecord : public CTypeRecord<CTypeMyRecord, CTypeInteger, CTypeString, CTypeString> | |
{ | |
static std::shared_ptr<CTypeMyRecord> _instance; | |
public: | |
static const std::shared_ptr<CTypeMyRecord> &Instance() { return _instance; } | |
CTypeMyRecord(): CTypeRecord(TFields({ true, "f1", CTypeInteger::Instance() }, | |
{ false, "f2", CTypeString::Instance() }, | |
{ false, "f3", CTypeString::Instance() } )) {} | |
}; | |
std::shared_ptr<CTypeMyRecord> CTypeMyRecord::_instance(std::make_shared<CTypeMyRecord>()); | |
typedef CRecord<CTypeMyRecord::CCurrentType> CMyRecord; | |
BOOST_AUTO_TEST_CASE( core_6_2_13_2_Ex_1 ) | |
{ | |
typedef CTypeSubValueOne<CTypeMyRecord> CTypeMyRecordOne; | |
typedef CTypeSubTypeList<CTypeMyRecord> CTypeMyRecordList; | |
std::shared_ptr<CTypeMyRecordList> MyRecordSub1(std::make_shared<CTypeMyRecordList>(CTypeMyRecordList({ | |
std::make_shared<CTypeMyRecordOne>(CMyRecord(CMyRecord::TValues(Omit, "user", "password"), | |
CTypeMyRecord::Instance())), | |
std::make_shared<CTypeMyRecordOne>(CMyRecord(CMyRecord::TValues(1, "User", "Password"), | |
CTypeMyRecord::Instance())) }))); | |
std::shared_ptr<CTypeMyRecordList> MyRecordSub2(std::make_shared<CTypeMyRecordList>(CTypeMyRecordList({ | |
MyRecordSub1, | |
std::make_shared<CTypeMyRecordOne>(CMyRecord(CMyRecord::TValues(2, "uname", "pswd"), | |
CTypeMyRecord::Instance())), | |
std::make_shared<CTypeMyRecordOne>(CMyRecord(CMyRecord::TValues(3, "Uname", "Pswd"), | |
CTypeMyRecord::Instance())) }))); | |
CMyRecord test0(MyRecordSub1); | |
BOOST_CHECK_EQUAL( test0.Defined(), false ); | |
CMyRecord test1(CMyRecord::TValues(1, "User", "Password"), MyRecordSub1); | |
BOOST_CHECK_EQUAL( test1.Defined(), true ); | |
BOOST_CHECK_EXCEPTION( CMyRecord test(CMyRecord::TValues(1, "user", "Password"), MyRecordSub1), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Record value 'RECORD' does not meet subtyping"; } ); | |
CMyRecord test2(CMyRecord::TValues(1, CString(), "Password"), MyRecordSub2); | |
BOOST_CHECK_EQUAL( test2.Defined(), false ); | |
BOOST_CHECK_NO_THROW( test2.Field<1>("User") ); | |
BOOST_CHECK_EXCEPTION( test2.Field<1>("usr"), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Record value 'RECORD' does not meet subtyping"; } ); | |
BOOST_CHECK_NO_THROW( test2 = CMyRecord::TValues(Omit, "user", "password") ); | |
BOOST_CHECK_NO_THROW( test2 = CMyRecord::TValues(3, "Uname", "Pswd") ); | |
CMyRecord test3(CMyRecord::TValues(CInteger(), CString(), "password"), MyRecordSub2); | |
BOOST_CHECK_EQUAL( test3.Defined(), false ); | |
BOOST_CHECK_EXCEPTION( test3 = CMyRecord::TValues(CInteger(), "User", CString()), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Record value 'RECORD' does not meet subtyping"; } ); | |
BOOST_CHECK_NO_THROW( test3 = CMyRecord::TValues(CInteger(), "User", "Password") ); | |
BOOST_CHECK_NO_THROW( test3 = CMyRecord::TValues(CInteger(), "user", "password") ); | |
BOOST_CHECK_EQUAL( test3.Defined(), false ); | |
BOOST_CHECK_NO_THROW( test3 = CMyRecord::TValues(Omit, CString(), CString()) ); | |
BOOST_CHECK_EQUAL( test3.Defined(), true ); | |
BOOST_CHECK_EXCEPTION( test3 = CMyRecord::TValues(Omit, "User", "Password"), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Record value 'RECORD' does not meet subtyping"; } ); | |
CMyRecord test4(CMyRecord::TValues(CInteger(), "user", "password"), MyRecordSub2); | |
BOOST_CHECK_NO_THROW( test4.Field<0>(Omit) ); | |
BOOST_CHECK_EQUAL( test4.Defined(), true ); | |
} | |
// void Test_6_2_13_2() | |
// { | |
// } | |
BOOST_AUTO_TEST_SUITE_END() | |
BOOST_AUTO_TEST_SUITE( types_compat ) | |
BOOST_AUTO_TEST_CASE( core_6_3_1_Ex_1 ) | |
{ | |
std::shared_ptr<CTypeIntegerRange> MyInteger(std::make_shared<CTypeIntegerRange>(0, 10)); | |
CInteger x; | |
CInteger y(MyInteger); | |
BOOST_CHECK_NO_THROW( y = 5 ); | |
BOOST_CHECK_NO_THROW( x = y ); | |
BOOST_CHECK_NO_THROW( x = 20 ); | |
BOOST_CHECK_EXCEPTION( y = x, | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '20' does not meet subtyping"; } ); | |
BOOST_CHECK_NO_THROW( x = 5 ); | |
BOOST_CHECK_NO_THROW( y = x ); | |
} | |
BOOST_AUTO_TEST_CASE( core_6_3_1_Ex_2 ) | |
{ | |
std::shared_ptr<CTypeFloatRange> PositiveFloats(std::make_shared<CTypeFloatRange>(0.0, CFloat::Infinity())); | |
CFloat x(PositiveFloats); | |
CFloat y; | |
BOOST_CHECK_NO_THROW( y = 5.0 ); | |
BOOST_CHECK_NO_THROW( x = y ); | |
BOOST_CHECK_NO_THROW( y = -20.0 ); | |
BOOST_CHECK_EXCEPTION( x = y, | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == "Float value '-20.000000' does not meet subtyping"; } ); | |
BOOST_CHECK_NO_THROW( y = CFloat::NaN() ); | |
BOOST_CHECK_EXCEPTION( x = y, | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == "Float value 'nan' does not meet subtyping"; } ); | |
} | |
class CTypeAType : public CTypeRecord<CTypeAType, CTypeIntegerRange, CTypeIntegerRange, CTypeFloat> | |
{ | |
static std::shared_ptr<CTypeAType> _instance; | |
public: | |
static const std::shared_ptr<CTypeAType> &Instance() { return _instance; } | |
CTypeAType(): CTypeRecord(TFields({ true, "a", std::make_shared<CTypeIntegerRange>(0, 10) }, | |
{ true, "b", std::make_shared<CTypeIntegerRange>(0, 10) }, | |
{ false, "c", CTypeFloat::Instance() } )) {} | |
}; | |
std::shared_ptr<CTypeAType> CTypeAType::_instance(std::make_shared<CTypeAType>()); | |
typedef CRecord<CTypeAType::CCurrentType> CAType; | |
class CTypeBType : public CTypeRecord<CTypeBType, CTypeInteger, CTypeIntegerRange, CTypeFloat> | |
{ | |
static std::shared_ptr<CTypeBType> _instance; | |
public: | |
static const std::shared_ptr<CTypeBType> &Instance() { return _instance; } | |
CTypeBType(): CTypeRecord(TFields({ true, "a", CTypeInteger::Instance() }, | |
{ true, "b", std::make_shared<CTypeIntegerRange>(0, 10) }, | |
{ false, "c", CTypeFloat::Instance() } )) {} | |
}; | |
std::shared_ptr<CTypeBType> CTypeBType::_instance(std::make_shared<CTypeBType>()); | |
typedef CRecord<CTypeBType::CCurrentType> CBType; | |
class CTypeCType : public CTypeRecord<CTypeCType, CTypeInteger, CTypeInteger, CTypeFloat> | |
{ | |
static std::shared_ptr<CTypeCType> _instance; | |
public: | |
static const std::shared_ptr<CTypeCType> &Instance() { return _instance; } | |
CTypeCType(): CTypeRecord(TFields({ true, "d", CTypeInteger::Instance() }, | |
{ true, "e", CTypeInteger::Instance() }, | |
{ false, "f", CTypeFloat::Instance() } )) {} | |
}; | |
std::shared_ptr<CTypeCType> CTypeCType::_instance(std::make_shared<CTypeCType>()); | |
typedef CRecord<CTypeCType::CCurrentType> CCType; | |
class CTypeDType : public CTypeRecord<CTypeDType, CTypeInteger, CTypeInteger, CTypeFloat> | |
{ | |
static std::shared_ptr<CTypeDType> _instance; | |
public: | |
static const std::shared_ptr<CTypeDType> &Instance() { return _instance; } | |
CTypeDType(): CTypeRecord(TFields({ true, "a", CTypeInteger::Instance() }, | |
{ true, "b", CTypeInteger::Instance() }, | |
{ true, "c", CTypeFloat::Instance() } )) {} | |
}; | |
std::shared_ptr<CTypeDType> CTypeDType::_instance(std::make_shared<CTypeDType>()); | |
typedef CRecord<CTypeDType::CCurrentType> CDType; | |
class CTypeEType : public CTypeRecord<CTypeEType, CTypeInteger, CTypeInteger, CTypeFloat, CTypeFloat> | |
{ | |
static std::shared_ptr<CTypeEType> _instance; | |
public: | |
static const std::shared_ptr<CTypeEType> &Instance() { return _instance; } | |
CTypeEType(): CTypeRecord(TFields({ true, "a", CTypeInteger::Instance() }, | |
{ true, "b", CTypeInteger::Instance() }, | |
{ false, "c", CTypeFloat::Instance() }, | |
{ true, "d", CTypeFloat::Instance() } )) {} | |
}; | |
std::shared_ptr<CTypeEType> CTypeEType::_instance(std::make_shared<CTypeEType>()); | |
typedef CRecord<CTypeEType::CCurrentType> CEType; | |
BOOST_AUTO_TEST_CASE( core_6_3_2_Ex_1 ) | |
{ | |
CAType MyVarA(CAType::TValues(CInteger(CTypeAType::Instance()->Type<0>()), | |
CInteger(1, CTypeAType::Instance()->Type<1>()), | |
CFloat()), | |
CTypeAType::Instance()); | |
CAType MyVarAA(CAType::TValues(CInteger(9, CTypeAType::Instance()->Type<0>()), | |
CInteger(9, CTypeAType::Instance()->Type<1>()), | |
CFloat(9.0)), | |
CTypeAType::Instance()); | |
CAType MyVarAAA(CAType::TValues(CInteger(CTypeAType::Instance()->Type<0>()), | |
CInteger(1, CTypeAType::Instance()->Type<1>()), | |
CFloat()), | |
CTypeAType::Instance()); | |
CBType MyVarB(CBType::TValues(Omit, | |
CInteger(2, CTypeBType::Instance()->Type<1>()), | |
CFloat(2.0)), | |
CTypeBType::Instance()); | |
CBType MyVarBB(CBType::TValues(CInteger(), | |
CInteger(2, CTypeBType::Instance()->Type<1>()), | |
CFloat()), | |
CTypeBType::Instance()); | |
CCType MyVarC(CCType::TValues(3, Omit, 3.0), | |
CTypeCType::Instance()); | |
CDType MyVarD(CDType::TValues(4, 4, 4.0), | |
CTypeDType::Instance()); | |
CEType MyVarE(CEType::TValues(5, 5, 4.0, Omit), | |
CTypeEType::Instance()); | |
BOOST_CHECK_EXCEPTION( MyVarAA = MyVarA, | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Cannot assign element with undefined value to a mandatory record element 'c'"; } ); | |
BOOST_CHECK_NO_THROW( MyVarA = MyVarAA ); | |
CInteger val(99); | |
BOOST_CHECK_EXCEPTION( MyVarA.Field<0>(val), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '99' does not meet subtyping"; } ); | |
BOOST_CHECK_EXCEPTION( MyVarA.Field<0>(33), | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Integer value '33' does not meet subtyping"; } ); | |
BOOST_CHECK_NO_THROW( MyVarA = MyVarB ); | |
BOOST_CHECK_EXCEPTION( MyVarA = MyVarBB, | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == | |
"Cannot assign element with undefined value to a mandatory record element 'c'"; } ); | |
BOOST_CHECK_EQUAL( MyVarBB.Field<0>().Omit(), false ); | |
BOOST_CHECK_NO_THROW( MyVarBB.Field<2>(3.14) ); | |
BOOST_CHECK_EQUAL( MyVarAAA.Field<0>().Omit(), false ); | |
BOOST_CHECK_NO_THROW( MyVarAAA = MyVarBB ); | |
BOOST_CHECK_EQUAL( MyVarAAA.Field<0>().Omit(), true ); | |
BOOST_CHECK_NO_THROW( MyVarC = MyVarB ); | |
BOOST_CHECK_EXCEPTION( MyVarA = MyVarD, | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == "Cannot assign records of different fields optionality"; } ); | |
// MyVarA = MyVarE; // should produce compile time error | |
BOOST_CHECK_NO_THROW( MyVarC.Field<0>(20) ); | |
BOOST_CHECK_EXCEPTION( MyVarA = MyVarC, | |
std::logic_error, | |
[](const std::logic_error &ex)->bool | |
{ return std::string(ex.what()) == "Integer value '20' does not meet subtyping"; } ); | |
} | |
BOOST_AUTO_TEST_SUITE_END() | |
// void TestUnion() | |
// { | |
// CMyUnion union1; | |
// std::cout << "Variants:"; | |
// std::for_each(union1.Names().begin(), union1.Names().end(), | |
// [](std::string *s) { std::cout << " " << *s; }); | |
// std::cout << std::endl; | |
// std::cout << "sizeof(myUnion): " << sizeof(union1) << std::endl; | |
// std::cout << "sizeof(shared_ptr): " << sizeof(std::make_shared<CString>()) << std::endl; | |
// try { | |
// union1.x(); | |
// std::cout << "Exception: FAIL" << std::endl; | |
// } | |
// catch(std::logic_error &ex) { | |
// std::cout << "Exception: PASS (" << ex.what() << ")" << std::endl; | |
// } | |
// TestNotDefined(union1); | |
// union1.y(AA(3)); | |
// TestDefined(union1); | |
// try { | |
// union1.x(); | |
// std::cout << "Exception: FAIL" << std::endl; | |
// } | |
// catch(std::logic_error &ex) { | |
// std::cout << "Exception: PASS (" << ex.what() << ")" << std::endl; | |
// } | |
// TestValue(union1.y(), CInteger(3)); | |
// CInteger a1(5); | |
// union1.y() = union1.y() + a1; | |
// TestValue(union1.y(), CInteger(8)); | |
// try { | |
// union1.x(CInteger(99)); | |
// std::cout << "Exception: FAIL" << std::endl; | |
// } | |
// catch(std::logic_error &ex) { | |
// std::cout << "Exception: PASS (" << ex.what() << ")" << std::endl; | |
// } | |
// try { | |
// union1.Get("test"); | |
// std::cout << "Exception: FAIL" << std::endl; | |
// } | |
// catch(std::logic_error &ex) { | |
// std::cout << "Exception: PASS (" << ex.what() << ")" << std::endl; | |
// } | |
// try { | |
// union1.Set("test", CInteger(99)); | |
// std::cout << "Exception: FAIL" << std::endl; | |
// } | |
// catch(std::logic_error &ex) { | |
// std::cout << "Exception: PASS (" << ex.what() << ")" << std::endl; | |
// } | |
// TestValue(dynamic_cast<const CInteger &>(union1.Get("y")), CInteger(8)); | |
// union1.Set("y", CInteger(13)); | |
// TestValue(union1.y(), CInteger(13)); | |
// CMyUnion union2; | |
// try { | |
// union2.Get("x"); | |
// std::cout << "Exception: FAIL" << std::endl; | |
// } | |
// catch(std::logic_error &ex) { | |
// std::cout << "Exception: PASS (" << ex.what() << ")" << std::endl; | |
// } | |
// try { | |
// union2.Set("test", CInteger(99)); | |
// std::cout << "Exception: FAIL" << std::endl; | |
// } | |
// catch(std::logic_error &ex) { | |
// std::cout << "Exception: PASS (" << ex.what() << ")" << std::endl; | |
// } | |
// try { | |
// union2.Set("x", CString("")); | |
// std::cout << "Exception: FAIL" << std::endl; | |
// } | |
// catch(std::logic_error &ex) { | |
// std::cout << "Exception: PASS (" << ex.what() << ")" << std::endl; | |
// } | |
// union2.Set("x", CInteger(13)); | |
// TestValue(union2.x(), CInteger(13)); | |
// CMyUnion union3(CMyUnion::CVariantx(Value<CInteger>(99))); | |
// TestValue(union3.x(), CInteger(99)); | |
// } | |
// /* *************************** ICMP module types *************************** */ | |
// CTypeRecord typeICMPPingDataType({ | |
// { false, "id" }, | |
// { false, "seqNumber" }, | |
// { true, "data" } }); | |
// class CICMPPingDataType : public CRecord<CInteger, CInteger, CString> { | |
// public: | |
// CICMPPingDataType(): CRecord(typeICMPPingDataType) {} | |
// CICMPPingDataType(TValues &&values): CRecord(typeICMPPingDataType, std::move(values)) {} | |
// CICMPPingDataType(const CICMPPingDataType &) = default; | |
// CICMPPingDataType(CICMPPingDataType &&) = default; | |
// virtual CICMPPingDataType *clone() const { return new CICMPPingDataType(*this); } | |
// CInteger &id() { return Field<0>(); } | |
// void id(const CInteger &value) { Field<0>(value); } | |
// void id(CInteger &&value) { Field<0>(std::move(value)); } | |
// CInteger &seqNumber() { return Field<1>(); } | |
// void seqNumber(const CInteger &value) { Field<1>(value); } | |
// void seqNumber(CInteger &&value) { Field<1>(std::move(value)); } | |
// CString &data() { return Field<2>(); } | |
// void data(const CString &value) { Field<2>(value); } | |
// void data(CString &&value) { Field<2>(std::move(value)); } | |
// }; | |
// CTypeUnion typeICMPDataType({ "ping", "pong" }); | |
// class CICMPDataType : public CUnion<CICMPPingDataType, AA> { | |
// public: | |
// typedef CUnionVariant<0, CICMPPingDataType> CVariantPing; | |
// CICMPDataType(): CUnion(typeICMPDataType) {} | |
// CICMPDataType(CVariantPing &&variant): CUnion(typeICMPDataType, std::move(variant)) {} | |
// CICMPDataType(const CICMPDataType &) = default; | |
// CICMPDataType(CICMPDataType &&) = default; | |
// ~CICMPDataType() = default; | |
// virtual CICMPDataType *clone() const { return new CICMPDataType(*this); } | |
// CICMPPingDataType &ping() { return *Variant<0>(); } | |
// void ping(const CICMPPingDataType &value) { Variant<0>(value); } | |
// void ping(CICMPPingDataType &&value) { Variant<0>(std::move(value)); } | |
// }; | |
// CTypeRecord typeICMPMsg({ | |
// { false, "msgType" }, | |
// { false, "code" }, | |
// { false, "crc" }, | |
// { false, "data" } }); | |
// class CICMPMsg : public CRecord<CInteger, CInteger, CInteger, CICMPDataType> { | |
// public: | |
// CICMPMsg(): CRecord(typeICMPMsg) {} | |
// CICMPMsg(TValues &&values): CRecord(typeICMPMsg, std::move(values)) {} | |
// CICMPMsg(const CICMPMsg &) = default; | |
// CICMPMsg(CICMPMsg &&) = default; | |
// ~CICMPMsg() = default; | |
// virtual CICMPMsg *clone() const { return new CICMPMsg(*this); } | |
// CInteger &msgType() { return Field<0>(); } | |
// void msgType(const CInteger &value) { Field<0>(value); } | |
// void msgType(CInteger &&value) { Field<0>(std::move(value)); } | |
// CInteger &code() { return Field<1>(); } | |
// void code(const CInteger &value) { Field<1>(value); } | |
// void code(CInteger &&value) { Field<1>(std::move(value)); } | |
// CInteger &crc() { return Field<2>(); } | |
// void crc(const CInteger &value) { Field<2>(value); } | |
// void crc(CInteger &&value) { Field<2>(std::move(value)); } | |
// CICMPDataType &data() { return Field<3>(); } | |
// void data(const CICMPDataType &value) { Field<3>(value); } | |
// void data(CICMPDataType &&value) { Field<3>(std::move(value)); } | |
// }; | |
// // template ICMPMsg t_EchoRequest(integer seqNum) := | |
// // { | |
// // msgType := 8, | |
// // code := 0, | |
// // crc := 0, | |
// // data := | |
// // { | |
// // ping := | |
// // { | |
// // id := 1234, | |
// // seqNumber := seqNum, | |
// // data := omit | |
// // } | |
// // } | |
// // } | |
// class Ct_EchoRequest : public CICMPMsg { | |
// public: | |
// Ct_EchoRequest(const CInteger &seqNum): | |
// CICMPMsg(CICMPMsg::TValues(Value<CInteger>(8), | |
// Value<CInteger>(0), | |
// Value<CInteger>(0), | |
// Value<CICMPDataType>(CICMPDataType::CVariantPing(Value<CICMPPingDataType>(CICMPPingDataType::TValues(Value<CInteger>(1234), | |
// Value<CInteger>(seqNum), | |
// Value<CString>(""))))))) {} | |
// }; | |
// void TestICMP() | |
// { | |
// Ct_EchoRequest t_EchoRequest(CInteger(1)); | |
// std::cout << "msgType: " << t_EchoRequest.msgType() << std::endl; | |
// std::cout << "code: " << t_EchoRequest.code() << std::endl; | |
// std::cout << "crc: " << t_EchoRequest.crc() << std::endl; | |
// std::cout << "data:" << std::endl; | |
// std::cout << " " << t_EchoRequest.data().Variant() << ":" << std::endl; | |
// std::cout << " id: " << t_EchoRequest.data().ping().id() << std::endl; | |
// std::cout << " seqNumber: " << t_EchoRequest.data().ping().seqNumber() << std::endl; | |
// std::cout << " data: " << t_EchoRequest.data().ping().data() << std::endl; | |
// } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _RECORD_H_ | |
#define _RECORD_H_ | |
#include "basic.h" | |
#include "utils.h" | |
#include <vector> | |
#include <string> | |
#include <algorithm> | |
typedef std::vector<std::string *> CFieldNamePtrArray; | |
template<typename RECORD_TYPE> | |
class CRecord; | |
/* *************************** Record type class *************************** */ | |
namespace detail { | |
template<typename TYPES_TUPLE, std::size_t N> | |
struct CTypeRecordHelper | |
{ | |
static void FieldNamePtrArraySet(TYPES_TUPLE &types, CFieldNamePtrArray &names) | |
{ | |
names.push_back(&std::get<std::tuple_size<TYPES_TUPLE>::value - N>(types).name); | |
CTypeRecordHelper<TYPES_TUPLE, N-1>::FieldNamePtrArraySet(types, names); | |
} | |
template<typename OTHER_TYPES_TUPLE> | |
static void OptionalityCheck(const TYPES_TUPLE &mine, const OTHER_TYPES_TUPLE &other) | |
{ | |
if(std::get<N-1>(mine).optional != std::get<N-1>(other).optional) | |
throw std::logic_error("Cannot assign records of different fields optionality"); | |
CTypeRecordHelper<TYPES_TUPLE, N-1>::OptionalityCheck(mine, other); | |
} | |
}; | |
template<typename TYPES_TUPLE> | |
struct CTypeRecordHelper<TYPES_TUPLE, 0> | |
{ | |
static void FieldNamePtrArraySet(TYPES_TUPLE &types, CFieldNamePtrArray &names) | |
{ | |
} | |
template<typename OTHER_TYPES_TUPLE> | |
static void OptionalityCheck(const TYPES_TUPLE &mine, const OTHER_TYPES_TUPLE &other) | |
{ | |
} | |
}; | |
template<typename TYPES_TUPLE> | |
void FieldNamePtrArraySet(TYPES_TUPLE &types, CFieldNamePtrArray &names) | |
{ | |
CTypeRecordHelper<TYPES_TUPLE, std::tuple_size<TYPES_TUPLE>::value>::FieldNamePtrArraySet(types, names); | |
} | |
template<typename TYPES_TUPLE, typename OTHER_TYPES_TUPLE> | |
void OptionalityCheck(const TYPES_TUPLE &mine, const OTHER_TYPES_TUPLE &other) | |
{ | |
CTypeRecordHelper<TYPES_TUPLE, std::tuple_size<TYPES_TUPLE>::value>::OptionalityCheck(mine, other); | |
} | |
template<std::size_t IDX, typename CHILD_TYPE, typename ...TYPES> | |
struct CRecursionChecker; | |
template<std::size_t IDX, typename CHILD_TYPE, typename HEAD, typename ...TYPES> | |
struct CRecursionChecker<IDX, CHILD_TYPE, HEAD, TYPES...> { | |
static const unsigned idx = CRecursionChecker<IDX+1, CHILD_TYPE, TYPES...>::idx; | |
}; | |
template<std::size_t IDX, typename TYPE, typename ...TYPES> | |
struct CRecursionChecker<IDX, TYPE, TYPE, TYPES...> { | |
static const unsigned idx = IDX; | |
}; | |
template<std::size_t IDX, typename CHILD_TYPE, typename HEAD> | |
struct CRecursionChecker<IDX, CHILD_TYPE, HEAD> { | |
static const unsigned idx = IDX + 1; | |
}; | |
template<std::size_t IDX, typename TYPE> | |
struct CRecursionChecker<IDX, TYPE, TYPE> { | |
static const unsigned idx = IDX; | |
}; | |
template<std::size_t IDX, typename TYPE> | |
struct CRecursionChecker<IDX, TYPE> { | |
static const unsigned idx = IDX; | |
}; | |
template<std::size_t IDX, typename TUPLE> | |
inline typename std::enable_if<std::tuple_size<TUPLE>::value && | |
IDX != std::tuple_size<TUPLE>::value>::type | |
RecursionCheck(const TUPLE &fields) | |
{ | |
if(!std::get<IDX>(fields).optional) | |
throw std::logic_error("Record infinite recursion detected (field '" + std::get<IDX>(fields).name + "' should be optional)"); | |
} | |
template<std::size_t IDX, typename TUPLE> | |
inline typename std::enable_if<!std::tuple_size<TUPLE>::value || | |
IDX == std::tuple_size<TUPLE>::value>::type | |
RecursionCheck(const TUPLE &fields) | |
{ | |
} | |
} // namespace detail | |
template<typename CHILD_TYPE, typename ...TYPES> | |
class CTypeRecord : public CType { | |
public: | |
typedef CTypeRecord<CHILD_TYPE, TYPES...> CCurrentType; | |
typedef CRecord<CCurrentType> CValueType; | |
template<typename TYPE> | |
struct TFieldData { | |
bool optional; | |
std::string name; | |
std::shared_ptr<const TYPE> type; | |
}; | |
typedef std::tuple<TFieldData<TYPES>...> TFields; | |
private: | |
TFields _fields; | |
CFieldNamePtrArray _fieldNamePtrArray; | |
public: | |
CTypeRecord() {} | |
CTypeRecord(TFields &&fields): | |
_fields(std::move(fields)) | |
{ | |
// check for types recursion | |
detail::RecursionCheck<detail::CRecursionChecker<0, CHILD_TYPE, TYPES...>::idx>(_fields); | |
// set field names array | |
detail::FieldNamePtrArraySet(_fields, _fieldNamePtrArray); | |
} | |
CTypeRecord *clone() const override { return new CTypeRecord(*this); } | |
virtual bool IsValid(const CValueType &value) const { return true; } | |
const CFieldNamePtrArray &FieldNames() const { return _fieldNamePtrArray; } | |
const TFields &Fields() const { return _fields; } | |
template<std::size_t IDX> | |
const typename std::tuple_element<IDX, TFields>::type &Field() const | |
{ | |
static_assert(IDX < sizeof...(TYPES), "Invalid type index provided"); | |
return std::get<IDX>(_fields); | |
} | |
template<std::size_t IDX> | |
const decltype(std::get<IDX>(std::declval<TFields>()).type) &Type() const | |
{ | |
static_assert(IDX < sizeof...(TYPES), "Invalid type index provided"); | |
return std::get<IDX>(_fields).type; | |
} | |
}; | |
namespace detail { | |
/* *************************** Record helpers *************************** */ | |
template<typename VALUES_TUPLE, std::size_t N> | |
struct CRecordHelper | |
{ | |
static bool Defined(const VALUES_TUPLE &values) | |
{ | |
return std::get<N-1>(values).Defined() && CRecordHelper<VALUES_TUPLE, N-1>::Defined(values); | |
} | |
template<typename VALUES_TUPLE_RIGHT> | |
static bool Equal(const VALUES_TUPLE &left, const VALUES_TUPLE_RIGHT &right) | |
{ | |
return (!std::get<N-1>(right).Defined() || std::get<N-1>(left) == std::get<N-1>(right)) && CRecordHelper<VALUES_TUPLE, N-1>::Equal(left, right); | |
} | |
// static const CValue &Get(const std::string &name, | |
// CFieldNamePtrArray::const_reverse_iterator it, | |
// const VALUES_TUPLE &values) | |
// { | |
// if(**it == name) | |
// return *std::get<N-1>(values); | |
// else | |
// return CRecordHelper<VALUES_TUPLE, N-1>::Get(name, ++it, values); | |
// } | |
// static void Set(const std::string &name, const CValue &value, | |
// CFieldNamePtrArray::const_reverse_iterator it, | |
// VALUES_TUPLE &values) | |
// { | |
// if(**it == name) | |
// ValueCopy(value, std::get<N-1>(values)); | |
// else | |
// CRecordHelper<VALUES_TUPLE, N-1>::Set(name, value, ++it, values); | |
// } | |
static void Assign(VALUES_TUPLE &&src, VALUES_TUPLE &dest) | |
{ | |
if(std::get<N-1>(src).Defined()) | |
std::get<N-1>(dest) = std::move(std::get<N-1>(src)); | |
CRecordHelper<VALUES_TUPLE, N-1>::Assign(std::move(src), dest); | |
} | |
template<typename TYPE> | |
static void AssignCheck(const VALUES_TUPLE &values, const TYPE &type) | |
{ | |
if(!std::get<N-1>(values).Defined() && !std::get<N-1>(type.Fields()).optional) | |
throw std::logic_error("Cannot assign element with undefined value to a mandatory record element '" + std::get<N-1>(type.Fields()).name + "'"); | |
CRecordHelper<VALUES_TUPLE, N-1>::AssignCheck(values, type); | |
} | |
template<typename SRC_TUPLE, typename TYPE> | |
static void Copy(SRC_TUPLE &&src, const TYPE &type, VALUES_TUPLE &dest) | |
{ | |
if(std::get<N-1>(src).Defined()) { | |
if(std::get<N-1>(src).Omit()) | |
std::get<N-1>(dest) = Omit; | |
else { | |
// copy value | |
if(std::is_rvalue_reference<decltype(src)>::value) | |
std::get<N-1>(dest) = std::move(std::get<N-1>(src)); | |
else | |
std::get<N-1>(dest) = std::get<N-1>(src); | |
} | |
} | |
else { | |
if(std::get<N-1>(type.Fields()).optional) | |
// if optional than set to omit | |
std::get<N-1>(dest) = Omit; | |
else | |
throw std::logic_error("Cannot assign element with undefined value to a mandatory record element '" + std::get<N-1>(type.Fields()).name + "' (should be found earlier!!!)"); | |
} | |
CRecordHelper<VALUES_TUPLE, N-1>::Copy(std::forward<SRC_TUPLE>(src), type, dest); | |
} | |
template<typename TYPE> | |
static void OmitCheck(const VALUES_TUPLE &values, const TYPE &type) | |
{ | |
if(std::get<N-1>(values).Omit() && !std::get<N-1>(type.Fields()).optional) | |
throw std::logic_error("Cannot set 'omit' for not optional record field '" + std::get<N-1>(type.Fields()).name + "'"); | |
CRecordHelper<VALUES_TUPLE, N-1>::OmitCheck(values, type); | |
} | |
}; | |
template<typename VALUES_TUPLE> | |
struct CRecordHelper<VALUES_TUPLE, 0> | |
{ | |
static bool Defined(const VALUES_TUPLE &values) | |
{ | |
return true; | |
} | |
template<typename VALUES_TUPLE_RIGHT> | |
static bool Equal(const VALUES_TUPLE &left, const VALUES_TUPLE_RIGHT &right) | |
{ | |
return true; | |
} | |
// static const CValue &Get(const std::string &name, | |
// CFieldNamePtrArray::const_reverse_iterator it, | |
// const VALUES_TUPLE &values) | |
// { | |
// throw std::logic_error("Field with name '" + name + "' not found"); | |
// } | |
// static void Set(const std::string &name, const CValue &value, | |
// CFieldNamePtrArray::const_reverse_iterator it, | |
// VALUES_TUPLE &values) | |
// { | |
// throw std::logic_error("Field with name '" + name + "' not found"); | |
// } | |
static void Assign(VALUES_TUPLE &&src, VALUES_TUPLE &dest) | |
{ | |
} | |
template<typename TYPE> | |
static void AssignCheck(const VALUES_TUPLE &values, const TYPE &type) | |
{ | |
} | |
template<typename SRC_TUPLE, typename TYPE> | |
static void Copy(SRC_TUPLE &&src, const TYPE &type, VALUES_TUPLE &dest) | |
{ | |
} | |
template<typename TYPE> | |
static void OmitCheck(const VALUES_TUPLE &values, const TYPE &type) | |
{ | |
} | |
}; | |
template<typename VALUES_TUPLE> | |
bool AllDefined(const VALUES_TUPLE &values) | |
{ | |
return CRecordHelper<VALUES_TUPLE, std::tuple_size<VALUES_TUPLE>::value>::Defined(values); | |
} | |
template<typename VALUES_TUPLE_LEFT, typename VALUES_TUPLE_RIGHT> | |
bool AllEqual(const VALUES_TUPLE_LEFT &left, const VALUES_TUPLE_RIGHT &right) | |
{ | |
return CRecordHelper<VALUES_TUPLE_LEFT, std::tuple_size<VALUES_TUPLE_LEFT>::value>::Equal(left, right); | |
} | |
// template<typename VALUES_TUPLE> | |
// const CValue &ValueGet(const std::string &name, | |
// const CFieldNamePtrArray &names, const VALUES_TUPLE &values) | |
// { | |
// return CRecordHelper<VALUES_TUPLE, std::tuple_size<VALUES_TUPLE>::value>::Get(name, names.rbegin(), values); | |
// } | |
// template<typename VALUES_TUPLE> | |
// void ValueSet(const std::string &name, const CValue &value, | |
// const CFieldNamePtrArray &names, VALUES_TUPLE &values) | |
// { | |
// CRecordHelper<VALUES_TUPLE, std::tuple_size<VALUES_TUPLE>::value>::Set(name, value, names.rbegin(), values); | |
// } | |
template<typename VALUES_TUPLE> | |
void ValuesAssign(VALUES_TUPLE &&src, VALUES_TUPLE &dest) | |
{ | |
CRecordHelper<VALUES_TUPLE, std::tuple_size<VALUES_TUPLE>::value>::Assign(std::move(src), dest); | |
} | |
template<typename TUPLE, typename TYPE> | |
void AssignCheck(const TUPLE &values, const TYPE &type) | |
{ | |
CRecordHelper<TUPLE, std::tuple_size<TUPLE>::value>::AssignCheck(values, type); | |
} | |
template<typename SRC_TUPLE, typename TYPE, typename DEST_TUPLE> | |
void ValuesCopy(SRC_TUPLE &&src, const TYPE &type, DEST_TUPLE &dest) | |
{ | |
AssignCheck(src, type); | |
CRecordHelper<DEST_TUPLE, std::tuple_size<DEST_TUPLE>::value>::Copy(std::forward<SRC_TUPLE>(src), type, dest); | |
} | |
template<typename TUPLE, typename TYPE> | |
void OmitCheck(const TUPLE &values, const TYPE &type) | |
{ | |
CRecordHelper<TUPLE, std::tuple_size<TUPLE>::value>::OmitCheck(values, type); | |
} | |
} // namespace detail | |
/* *************************** Record base class *************************** */ | |
template<typename CHILD_TYPE, typename ...TYPES> | |
class CRecord<CTypeRecord<CHILD_TYPE, TYPES...> > : public CValue { | |
public: | |
typedef CHILD_TYPE CChildType; | |
typedef std::tuple<typename TYPES::CValueType...> TValues; | |
private: | |
const std::shared_ptr<CChildType> _type; | |
std::unique_ptr<TValues> _values; | |
public: | |
template<size_t IDX> | |
typename std::tuple_element<IDX, TValues>::type &Field() | |
{ | |
if(!_values) | |
throw std::logic_error("Cannot get fields of undefined record"); | |
return std::get<IDX>(*_values); | |
} | |
template<size_t IDX, typename VALUE> | |
void Field(VALUE &&value) | |
{ | |
if(!_values) | |
throw std::logic_error("Cannot set fields of undefined record"); | |
std::get<IDX>(*_values) = std::forward<VALUE>(value); | |
if(std::get<IDX>(*_values).Omit() && !_type->Field<IDX>().optional) | |
throw std::logic_error("Cannot set 'omit' for not optional record field '" + _type->Field<IDX>().name + "'"); | |
if(!_type->IsValid(*this)) | |
throw std::logic_error("Record value '" + this->String() + "' does not meet subtyping"); | |
} | |
public: | |
CRecord(const std::shared_ptr<CChildType> &type): | |
_type(type) | |
{ | |
} | |
CRecord(TValues &&values, const std::shared_ptr<CChildType> &type): | |
_type(type), _values(make_unique<TValues>(std::move(values))) | |
{ | |
if(_values) | |
detail::OmitCheck(*_values, *_type); | |
if(!_type->IsValid(*this)) | |
throw std::logic_error("Record value '" + this->String() + "' does not meet subtyping"); | |
} | |
CRecord(TOmit, const std::shared_ptr<CChildType> &type): | |
CValue(::Omit), _type(type) | |
{ | |
} | |
CRecord(const CRecord &other): | |
CValue(other), _type(other._type), _values(other._values ? make_unique<TValues>(*other._values) : nullptr) | |
{ | |
} | |
CRecord(CRecord &&other): | |
_type(std::move(other._type)) | |
{ | |
swap(*this, other); | |
} | |
~CRecord() = default; | |
CRecord &operator=(CRecord other) | |
{ | |
if(other._values) | |
detail::OmitCheck(*other._values, *_type); | |
if(!_type->IsValid(other)) | |
throw std::logic_error("Record value '" + other.String() + "' does not meet subtyping"); | |
if(other._values) | |
detail::AssignCheck(*other._values, *_type); | |
swap(*this, other); | |
return *this; | |
} | |
CRecord &operator=(TValues &&values) | |
{ | |
if(_values) | |
detail::ValuesAssign(std::move(values), *_values); | |
else | |
_values = make_unique<TValues>(std::move(values)); | |
if(_values) | |
detail::OmitCheck(*_values, *_type); | |
if(!_type->IsValid(*this)) | |
throw std::logic_error("Record value '" + this->String() + "' does not meet subtyping"); | |
return *this; | |
} | |
template<typename OTHER_CHILD_TYPE, typename ...OTHER_TYPES> | |
CRecord &operator=(const CRecord<CTypeRecord<OTHER_CHILD_TYPE, OTHER_TYPES...> > &other) | |
{ | |
// check sizes | |
static_assert(sizeof...(TYPES) == sizeof...(OTHER_TYPES), "Cannot assign a record with different number of fields"); | |
// check optionality of record fields | |
detail::OptionalityCheck(_type->Fields(), other.Type().Fields()); | |
// copy data | |
detail::ValuesCopy(other.Values(), *_type, *_values); | |
CValue::operator=(other); | |
if(_values) | |
detail::OmitCheck(*_values, *_type); | |
if(!_type->IsValid(*this)) | |
throw std::logic_error("Record value '" + this->String() + "' does not meet subtyping"); | |
return *this; | |
} | |
CRecord &operator=(TOmit) | |
{ | |
CValue::operator=(::Omit); | |
return *this; | |
} | |
friend void swap(CRecord &first, CRecord &second) noexcept | |
{ | |
using std::swap; | |
// swap(first._type, second._type); | |
swap(first._values, second._values); | |
swap(static_cast<CValue &>(first), second); | |
} | |
CRecord *clone() const override { return new CRecord(*this); } | |
const CChildType &Type() const override { return *_type; } | |
std::string String() const override { return "RECORD"; } | |
bool Defined() const override { return (_values && detail::AllDefined(*_values)) || Omit(); } | |
const TValues &Values() const { return *_values; } | |
// const CValue &Get(const std::string &name) const | |
// { | |
// return detail::ValueGet(name, _type.FieldNames(), _values); | |
// } | |
// void Set(const std::string &name, const CValue &value) | |
// { | |
// detail::ValueSet(name, value, _type.FieldNames(), _values); | |
// } | |
const CFieldNamePtrArray &Names() const | |
{ | |
return _type->FieldNames(); | |
} | |
template<typename OTHER_CHILD_TYPE, typename ...OTHER_TYPES> | |
friend bool operator==(const CRecord &left, const CRecord<CTypeRecord<OTHER_CHILD_TYPE, OTHER_TYPES...> > &right) | |
{ | |
static_assert(sizeof...(TYPES) == sizeof...(OTHER_TYPES), "Cannot compare records of different sizes"); | |
if(left.Omit() || right.Omit()) | |
return left.Omit() == right.Omit(); | |
else | |
return detail::AllEqual(*left._values, *right._values); | |
} | |
}; | |
#endif /* _RECORD_H_ */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _SUBTYPE_H_ | |
#define _SUBTYPE_H_ | |
template<typename TYPE> | |
class CTypeSubValueOne : public TYPE { | |
const typename TYPE::CValueType _value; | |
public: | |
CTypeSubValueOne(typename TYPE::CValueType &&value): _value(std::move(value)) {} | |
bool IsValid(const typename TYPE::CValueType &value) const override | |
{ | |
return TYPE::IsValid(value) && _value == value; | |
} | |
// CTypeSubValueOne *clone() const override { return new CTypeSubValueOne(*this); } | |
}; | |
template<typename TYPE> | |
class CTypeSubValueRange : public TYPE { | |
const typename TYPE::CValueType _valueMin; | |
const bool _minExclusive; | |
const typename TYPE::CValueType _valueMax; | |
const bool _maxExclusive; | |
public: | |
CTypeSubValueRange(typename TYPE::CValueType &&valueMin, typename TYPE::CValueType &&valueMax, | |
bool minExclusive = false, bool maxExclusive = false): | |
_valueMin(std::move(valueMin)), _minExclusive(minExclusive), | |
_valueMax(std::move(valueMax)), _maxExclusive(maxExclusive) | |
{ | |
} | |
bool IsValid(const typename TYPE::CValueType &value) const override | |
{ | |
return TYPE::IsValid(value) && | |
((_valueMin < value && _valueMax > value) || | |
(!_minExclusive && _valueMin == value) || | |
(!_maxExclusive && _valueMax == value)); | |
} | |
}; | |
template<typename TYPE> | |
class CTypeSubTypeList : public TYPE { | |
public: | |
typedef std::vector<std::shared_ptr<TYPE> > CTypeList; | |
private: | |
const CTypeList _list; | |
public: | |
CTypeSubTypeList(CTypeList &&list) : _list(std::move(list)) {} | |
bool IsValid(const typename TYPE::CValueType &value) const override | |
{ | |
return TYPE::IsValid(value) && | |
std::any_of(_list.begin(), _list.end(), | |
[&value](const std::shared_ptr<TYPE> &t) { return t->IsValid(value); }); | |
} | |
// CTypeSubTypeList *clone() const override { return new CTypeSubTypeList(*this); } | |
}; | |
typedef CTypeSubValueOne<CTypeInteger> CTypeIntegerOne; | |
typedef CTypeSubValueRange<CTypeInteger> CTypeIntegerRange; | |
typedef CTypeSubTypeList<CTypeInteger> CTypeIntegerList; | |
typedef CTypeSubValueOne<CTypeFloat> CTypeFloatOne; | |
typedef CTypeSubValueRange<CTypeFloat> CTypeFloatRange; | |
typedef CTypeSubTypeList<CTypeFloat> CTypeFloatList; | |
#endif /* _SUBTYPE_H_ */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _UNION_H_ | |
#define _UNION_H_ | |
#include "basic.h" | |
#include "utils.h" | |
#include <set> | |
#include <vector> | |
#include <algorithm> | |
typedef std::set<std::string *> CVariantNamePtrSet; | |
template<class ...VALUES> | |
class CUnion; | |
/* *************************** Union type class *************************** */ | |
template<typename CHILD_TYPE, typename ...TYPES> | |
class CTypeUnion : public CType { | |
public: | |
typedef CTypeUnion<CHILD_TYPE, TYPES...> type; | |
typedef CUnion<type> CValue; | |
struct TVariantData { | |
std::string name; | |
std::shared_ptr<const TYPE> type; | |
}; | |
typedef std::tuple<TVariantData<TYPES>...> TVariants; | |
private: | |
TVariants _variants; | |
CVariantNamePtrSet _variantNamePtrSet; | |
public: | |
CTypeUnion(TVariants &&variants): | |
_variants(std::move(variants)) | |
{ | |
std::transform(_variantNameArray.begin(), _variantNameArray.end(), | |
std::inserter(_variantNamePtrSet, _variantNamePtrSet.begin()), | |
[](std::string &s) { return &s; }); | |
} | |
virtual CTypeUnion *clone() const { return new CTypeUnion(*this); } | |
const CVariantNamePtrSet &VariantNamePtrs() const { return _variantNamePtrSet; } | |
const CVariantNameArray &VariantNames() const { return _variantNameArray; } | |
}; | |
/* *************************** Union helpers *************************** */ | |
template<std::size_t I, class UNION> | |
struct CUnionElement; | |
template<std::size_t I, class HEAD, class ...TAIL> | |
struct CUnionElement<I, CUnion<HEAD, TAIL...> >: CUnionElement<I-1, CUnion<TAIL...> > | |
{ | |
}; | |
template<class HEAD, class ...TAIL> | |
struct CUnionElement<0, CUnion<HEAD, TAIL...> > | |
{ | |
typedef CValuePtr<HEAD> type; | |
}; | |
template<std::size_t I, class VALUE> | |
class CUnionVariant | |
{ | |
CValuePtr<VALUE> _value; | |
public: | |
CUnionVariant(CValuePtr<VALUE> &&value): _value(std::move(value)) {} | |
CValuePtr<VALUE> &Value() { return _value; } | |
}; | |
namespace detail { | |
template<std::size_t I, class ...VALUES> | |
typename CUnionElement<I, CUnion<VALUES...> >::type &Get(CUnion<VALUES...> &u) | |
{ | |
return *reinterpret_cast<typename CUnionElement<I, CUnion<VALUES...> >::type *>(&u._union); | |
} | |
template<std::size_t I, class ...VALUES> | |
const typename CUnionElement<I, CUnion<VALUES...> >::type &Get(const CUnion<VALUES...> &u) | |
{ | |
return *reinterpret_cast<const typename CUnionElement<I, CUnion<VALUES...> >::type *>(&u._union); | |
} | |
/* *************************** Union implementation *************************** */ | |
template<class ...VALUES> | |
struct CUnionImpl; | |
template<class HEAD, class ...TAIL> | |
struct CUnionImpl<HEAD, TAIL...> { | |
typedef CValuePtr<HEAD> CVariantType; | |
typedef CUnionImpl<TAIL...> CRestType; | |
union { | |
CVariantType _value; | |
CRestType _rest; | |
}; | |
CUnionImpl() {} | |
~CUnionImpl() {} | |
void Release(int variant) | |
{ | |
if(!variant) | |
_value.~CVariantType(); | |
else | |
_rest.Release(variant - 1); | |
} | |
const CValue &Get(int variant) const | |
{ | |
if(!variant) | |
return *_value; | |
else | |
return _rest.Get(variant - 1); | |
} | |
template<typename VALUE> | |
void Set(int variant, bool init, VALUE &&value) | |
{ | |
if(!variant) { | |
if(init) | |
// allocate resources explicitly | |
new(&_value) CVariantType; | |
ValueClone(std::forward<VALUE>(value), _value); | |
} | |
else | |
Set(variant - 1, init, std::forward<VALUE>(value)); | |
} | |
}; | |
template<class HEAD> | |
class CUnionImpl<HEAD> { | |
public: | |
typedef CValuePtr<HEAD> CVariantType; | |
private: | |
CVariantType _value; | |
public: | |
CUnionImpl() {} | |
~CUnionImpl() {} | |
void Release(int variant) | |
{ | |
if(!variant) | |
_value.~CVariantType(); | |
else | |
throw std::logic_error("Wrong union variant provided"); | |
} | |
const CValue &Get(int variant) const | |
{ | |
if(!variant) | |
return *_value; | |
else | |
throw std::logic_error("Wrong union variant provided"); | |
} | |
template<typename VALUE> | |
void Set(int variant, bool init, VALUE &&value) | |
{ | |
if(!variant) { | |
if(init) | |
// allocate resources explicitly | |
new(&_value) CVariantType; | |
ValueClone(std::forward<VALUE>(value), _value); | |
} | |
else | |
throw std::logic_error("Wrong union variant provided"); | |
} | |
}; | |
} | |
/* *************************** Union base class *************************** */ | |
template<class ...VALUES> | |
class CUnion : public CValue { | |
const CTypeUnion &_type; | |
int _variant; | |
public: | |
typedef CTypeUnion CType; | |
detail::CUnionImpl<VALUES...> _union; | |
protected: | |
template<std::size_t IDX> | |
typename CUnionElement<IDX, CUnion>::type &Variant() | |
{ | |
if(_variant == -1) | |
throw std::logic_error("Cannot get '" + _type.VariantNames()[IDX] + "' value: union variant not set"); | |
if(_variant != IDX) | |
throw std::logic_error("Cannot get '" + _type.VariantNames()[IDX] + "' value: union set for variant '" + _type.VariantNames()[_variant] + "'"); | |
return detail::Get<IDX>(*this); | |
} | |
template<std::size_t IDX, class VALUE> | |
void Variant(VALUE &&value) | |
{ | |
if(_variant != -1 && _variant != IDX) | |
throw std::logic_error("Cannot set '" + _type.VariantNames()[IDX] + "' value: union set for variant '" + _type.VariantNames()[_variant] + "'"); | |
if(_variant == -1) { | |
// allocate resources explicitly | |
typedef typename CUnionElement<IDX, CUnion>::type CVariantType; | |
CVariantType &var = detail::Get<IDX>(*this); | |
new(&var) CVariantType; | |
// remember union variant | |
_variant = IDX; | |
} | |
ValueClone(std::forward<VALUE>(value), detail::Get<IDX>(*this)); | |
} | |
public: | |
CUnion(CTypeUnion &type): | |
_type(type), _variant(-1) | |
{ | |
if(_type.VariantNames().size() != sizeof...(VALUES)) | |
throw std::logic_error("Union names and values lists of different size"); | |
} | |
template<std::size_t IDX, class VALUE> | |
CUnion(CTypeUnion &type, CUnionVariant<IDX, VALUE> &&variant): | |
_type(type), _variant(-1) | |
{ | |
Variant<IDX>(std::move(variant.Value())); | |
} | |
CUnion(const CUnion &src): | |
CValue(src), _type(src._type), _variant(src._variant) | |
{ | |
if(_variant != -1) | |
_union.Set(_variant, true, src._union._value); | |
} | |
CUnion(CUnion &&src): | |
CValue(std::move(src)), _type(std::move(src._type)), _variant(std::move(src._variant)) | |
{ | |
if(_variant != -1) | |
_union.Set(_variant, true, std::move(src._union._value)); | |
} | |
~CUnion() | |
{ | |
if(_variant != -1) | |
// destroy explicitely | |
_union.Release(_variant); | |
} | |
CUnion &operator=(const CUnion &) = delete; | |
CUnion &operator=(CUnion &&) = delete; | |
virtual const CTypeUnion &Type() const { return _type; } | |
virtual std::string String() const { return "UNION"; } | |
bool Defined() const | |
{ | |
return _variant != -1; | |
} | |
const CValue &Get(const std::string &variant) const | |
{ | |
if(_variant == -1) | |
throw std::logic_error("Cannot get '" + variant + "' value: union variant not set"); | |
// check if current variant | |
if(_type.VariantNames()[_variant] != variant) | |
throw std::logic_error("Cannot get '" + variant + "' value: union set for variant '" + _type.VariantNames()[_variant] + "'"); | |
// get value | |
return _union.Get(_variant); | |
} | |
void Set(const std::string &variant, const CValue &value) | |
{ | |
// check if current variant | |
if(_variant != -1 && _type.VariantNames()[_variant] != variant) | |
throw std::logic_error("Cannot set '" + variant + "' value: union set for variant '" + _type.VariantNames()[_variant] + "'"); | |
int var = _variant; | |
bool init = false; | |
if(var == -1) { | |
// find a variant index | |
CTypeUnion::CVariantNameArray::const_iterator it = _type.VariantNames().begin(); | |
for(var=0; it!=_type.VariantNames().end(); ++it, ++var) | |
if(*it == variant) | |
break; | |
if(it == _type.VariantNames().end()) | |
throw std::logic_error("'" + variant + "' is not a valid union variant"); | |
init = true; | |
} | |
// set value | |
_union.Set(var, init, value); | |
// set variant | |
_variant = var; | |
} | |
const std::string &Variant() | |
{ | |
if(_variant == -1) | |
throw std::logic_error("Union variant not set"); | |
return _type.VariantNames()[_variant]; | |
} | |
const CVariantNamePtrSet &Names() const | |
{ | |
return _type.VariantNamePtrs(); | |
} | |
}; | |
#endif /* _UNION_H_ */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _UTILS_H_ | |
#define _UTILS_H_ | |
#include <memory> | |
// lacking std wrappers | |
template<class Container> | |
inline auto cbegin(Container &cont) -> decltype(cont.begin()) | |
{ return cont.cbegin(); } | |
template<class T, size_t N> | |
inline const T *cbegin(T (&arr)[N]) | |
{ return arr; } | |
template<class Container> | |
inline auto cend(Container &cont) -> decltype(cont.end()) | |
{ return cont.cend(); } | |
template<class T, size_t N> | |
inline const T *cend(T (&arr)[N]) | |
{ return arr + N; } | |
template<typename T, typename ...Args> | |
inline std::unique_ptr<T> make_unique(Args&&... args) | |
{ | |
return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); | |
} | |
#endif /* _UTILS_H_ */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment