Skip to content

Instantly share code, notes, and snippets.

@mpusz
Created November 3, 2011 21:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mpusz/1337823 to your computer and use it in GitHub Desktop.
Save mpusz/1337823 to your computer and use it in GitHub Desktop.
Simple TTCN structured types implementation
*.user
build/*
#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_ */
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...")
#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;
// }
#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_ */
#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_ */
#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_ */
#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