CSValue.h
// CSValue.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2012
#include "Ceda/cxUtils/xstring.h"
#include "Ceda/cxUtils/MathExt.h"
#include "Ceda/cxUtils/CedaAssert.h"
#include "Ceda/cxUtils/IException.h"
@import "Ceda/cxObject/Object.h"
@import "Ceda/cxObject/ConstructDestructCopyAssignReflectedVariable.h"
@import "cxCedaScript.h"
/*
A convention for coercions
--------------------------
We want to establish a convention for coercions amongst C++ value types. It is proposed that
the following function be defined for lots of combinations of T1,T2
bool Coerce(T1& lhs, const T2& rhs, bool allowExplicitConversions = false);
E.g.
inline bool Coerce(bool& lhs, bool rhs, bool allowExplicitConversions = false)
{
lhs = rhs;
return true;
}
inline bool Coerce(bool& lhs, int64 rhs, bool allowExplicitConversions = false)
{
if (allowExplicitConversions)
{
lhs = (rhs != 0);
return true;
}
else
{
return false;
}
}
Alternatively we define two functions
bool Assign(T& lhs, T rhs);
bool Coerce(T& lhs, T rhs);
E.g.
inline bool Assign(bool& lhs, int64 rhs) { return false; }
inline bool Coerce(bool& lhs, int64 rhs) { lhs = (rhs != 0); return true; }
Showing error messages when a coercion fails
--------------------------------------------
There is no need for Coerce() to be concerned with throwing exceptions. Instead we can write
a function like this
template<typename T1, typename T2>
void CoerceOrThrow(T1& lhs, const T2& rhs, ConstStringZ dstType, bool allowExplicitConversions = false)
{
if (!Coerce(lhs,rhs,allowExplicitConversions))
{
throw CSValueException(cxMakeString2("No implicit conversion from value " << rhs << " to " << dstType));
}
}
*/
namespace ceda
{
template <typename T1, typename T2>
inline bool Assign(T1& lhs, const T2& rhs) { return false; }
template <typename T>
inline bool Assign(T& lhs, const T& rhs) { lhs = rhs; return true; }
@scope
{
// Returns true if the values of type T1 are a subset of the values of type T2
@def bool IsNumericSubType(T1,T2) =
{
@let bool v = false
@for ( (t1,t2) in
[
(int8,int8),
(int16,int8),
(int16,int16),
(int32,int8),
(int32,int16),
(int32,int32),
(int64,int8),
(int64,int16),
(int64,int32),
(int64,int64),
(uint8,uint8),
(uint16,uint8),
(uint16,uint16),
(uint32,uint8),
(uint32,uint16),
(uint32,uint32),
(uint64,uint8),
(uint64,uint16),
(uint64,uint32),
(uint64,uint64),
(float32,int8),
(float32,int16),
(float32,uint8),
(float32,uint16),
(float32,float32),
(float64,int8),
(float64,int16),
(float64,int32),
(float64,uint8),
(float64,uint16),
(float64,uint32),
(float64,float32),
(float64,float64)
] )
{
@if (@str(t1)==@str(T2) && @str(t2)==@str(T1)) @let v=true
}
v
}
@for (T1 in [int8,int16,int32,int64,uint8,uint16,uint32,uint64,float32,float64])
{
@for (T2 in [int8,int16,int32,int64,uint8,uint16,uint32,uint64,float32,float64])
{
inline bool Assign(T1& lhs, T2 rhs)
{
@if (IsNumericSubType(T2,T1))
{
lhs = rhs;
return true;
}
@else
{
lhs = (T1) rhs;
return IsNumericallyEqual(lhs,rhs);
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// ReflectedValue
$struct+ ReflectedValue
{
ReflectedValue();
~ReflectedValue();
ReflectedValue(const ReflectedValue& rhs);
ReflectedValue& operator=(const ReflectedValue& rhs);
void swap(ReflectedValue& rhs);
bool operator==(const ReflectedValue& rhs) const;
bool operator!=(const ReflectedValue& rhs) const { return !operator==(rhs); }
bool operator<(const ReflectedValue& rhs) const;
bool operator<=(const ReflectedValue& rhs) const { return !(rhs < *this); }
bool operator>(const ReflectedValue& rhs) const { return rhs < *this; }
bool operator>=(const ReflectedValue& rhs) const { return !(*this < rhs); }
template <typename T>
bool Coerce(T& v, bool allowExplicitConversions = false) const
{
return ceda::Coerce(v,m_rbc,m_data.data(),allowExplicitConversions);
}
ReflectionByteCode m_rbc;
xvector<octet_t> m_data;
};
template <typename T>
bool Assign(T& lhs, const ReflectedValue& rhs)
{
return rhs.Coerce(lhs);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// CSValueException
struct @api CSValueException : public IException
{
CSValueException(ConstStringZ description) :
m_description(description)
{
}
virtual void Write(xostream& os) const { os << m_description; }
xstring m_description;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// CSValue
enum ECSValue
{
CSV_VOID,
CSV_BOOL,
CSV_INT64,
CSV_FLOAT64,
CSV_STRING8,
CSV_REFLECTED_VALUE
};
$variant+ CSValue
{
void;
bool;
int64;
float64;
string8;
ReflectedValue;
};
@api bool Assign(bool& v, const CSValue& csv);
@api bool Assign(int8& v, const CSValue& csv);
@api bool Assign(int16& v, const CSValue& csv);
@api bool Assign(int32& v, const CSValue& csv);
@api bool Assign(int64& v, const CSValue& csv);
@api bool Assign(uint8& v, const CSValue& csv);
@api bool Assign(uint16& v, const CSValue& csv);
@api bool Assign(uint32& v, const CSValue& csv);
@api bool Assign(uint64& v, const CSValue& csv);
@api bool Assign(float32& v, const CSValue& csv);
@api bool Assign(float64& v, const CSValue& csv);
@api bool Assign(string8& v, const CSValue& csv);
// Throws exceptions if assignment not possible
// What? How can it not be possible? And why don't we just execute lhs=rhs?
@api void Assign(CSValue& lhs, const CSValue& rhs);
@api bool Coerce(bool& v, const CSValue& csv, bool allowExplicitConversions = false);
@api bool Coerce(int8& v, const CSValue& csv, bool allowExplicitConversions = false);
@api bool Coerce(int16& v, const CSValue& csv, bool allowExplicitConversions = false);
@api bool Coerce(int32& v, const CSValue& csv, bool allowExplicitConversions = false);
@api bool Coerce(int64& v, const CSValue& csv, bool allowExplicitConversions = false);
@api bool Coerce(uint8& v, const CSValue& csv, bool allowExplicitConversions = false);
@api bool Coerce(uint16& v, const CSValue& csv, bool allowExplicitConversions = false);
@api bool Coerce(uint32& v, const CSValue& csv, bool allowExplicitConversions = false);
@api bool Coerce(uint64& v, const CSValue& csv, bool allowExplicitConversions = false);
@api bool Coerce(float32& v, const CSValue& csv, bool allowExplicitConversions = false);
@api bool Coerce(float64& v, const CSValue& csv, bool allowExplicitConversions = false);
@api bool Coerce(string8& v, const CSValue& csv, bool allowExplicitConversions = false);
template <typename T>
inline bool Coercible(const CSValue& csv, bool allowExplicitConversions = false)
{
T val;
return Coerce(val,csv,allowExplicitConversions);
}
inline const char* _GetTypeName(const bool*) { return "bool"; }
inline const char* _GetTypeName(const int8*) { return "int8"; }
inline const char* _GetTypeName(const int16*) { return "int16"; }
inline const char* _GetTypeName(const int32*) { return "int32"; }
inline const char* _GetTypeName(const int64*) { return "int64"; }
inline const char* _GetTypeName(const uint8*) { return "uint8"; }
inline const char* _GetTypeName(const uint16*) { return "uint16"; }
inline const char* _GetTypeName(const uint32*) { return "uint32"; }
inline const char* _GetTypeName(const uint64*) { return "uint64"; }
inline const char* _GetTypeName(const float32*) { return "float32"; }
inline const char* _GetTypeName(const float64*) { return "float64"; }
inline const char* _GetTypeName(const string8*) { return "string8"; }
template <typename T>
inline const char* GetTypeName() { return _GetTypeName( (const T*) 0 ); }
template <typename T>
[[noreturn]] void ThrowNoImplicitConversionFromValue(T v, ConstStringZ dstType)
{
throw CSValueException(cxMakeString2("No implicit conversion from value " << v << " to " << dstType));
}
// As<T>(CSValue) is intended to be called by the cedascript parser. It throws an appropriate
// exception if a conversion is not possible.
template <typename T>
T As(const CSValue& csv, bool allowExplicitConversions = false)
{
T v;
if (!Coerce(v,csv,allowExplicitConversions))
{
ThrowNoImplicitConversionFromValue(csv, GetTypeName<T>());
}
return v;
}
// Assign x = x(y) where y represents a comma separated list of arguments
@api void ApplyFunction(CSValue& x,const xvector<CSValue>& y);
// Assign x = x.y
@api void ApplyDot(CSValue& x, const xstring& y);
// Assign x = x[i]
@api void ApplySubscript(CSValue& x, const CSValue& i);
// Assign x = len(x)
@api void ApplyLen(CSValue& x);
// Unary operations
@api void ApplyUnaryPlus(CSValue& x); // x = +x
@api void ApplyUnaryMinus(CSValue& x); // x = -x
@api void ApplyUnaryNot(CSValue& x); // x = !x
@api void ApplyUnaryBitComplement(CSValue& x); // x = ~x
// Binary arithmetic operations
@api const CSValue operator+(const CSValue& x, const CSValue& y);
@api const CSValue operator-(const CSValue& x, const CSValue& y);
@api const CSValue operator*(const CSValue& x, const CSValue& y);
@api const CSValue operator/(const CSValue& x, const CSValue& y);
@api const CSValue operator%(const CSValue& x, const CSValue& y);
// Binary shift operations
@api const CSValue operator<<(const CSValue& x, const CSValue& y);
@api const CSValue operator>>(const CSValue& x, const CSValue& y);
// Binary logical operations
@api const CSValue operator||(const CSValue& x, const CSValue& y);
@api const CSValue operator&&(const CSValue& x, const CSValue& y);
// Binary bitwise operations
@api const CSValue operator|(const CSValue& x, const CSValue& y);
@api const CSValue operator&(const CSValue& x, const CSValue& y);
@api const CSValue operator^(const CSValue& x, const CSValue& y);
/*
// Unary operations
@api bool operator!(const CSValue& x);
@api CSValue operator+(const CSValue& x);
@api CSValue operator-(const CSValue& x);
@api CSValue operator~(const CSValue& x);
*/
// Comparision assuming value semantics (i.e. equivalence of value only)
@api bool IsEqual(const CSValue& x, const CSValue& y);
@api bool IsLess(const CSValue& x, const CSValue& y);
@api CSValue Power(const CSValue& x, const CSValue& y);
} // namespace ceda