From ae91a7cf4f325749bb46c2fb2df1d141d710b17f Mon Sep 17 00:00:00 2001
From: Ramin Yaghoubzadeh <ryaghoubzadeh@uni-bielefeld.de>
Date: Thu, 26 Feb 2015 17:57:06 +0100
Subject: [PATCH] C++: stricter type conversions (to be discussed)

PayloadTypeConversionError thrown if (trimmed) string parse is
incomplete; comparison operators == and != catch this error though.
---
 .../cpp/include/ipaaca/ipaaca-definitions.h   |  9 ++++
 ipaacalib/cpp/include/ipaaca/ipaaca-payload.h | 22 ++++++++
 ipaacalib/cpp/src/ipaaca-json.cc              | 16 ++++++
 ipaacalib/cpp/src/ipaaca-payload.cc           | 51 ++++++++++++++-----
 ipaacalib/cpp/src/ipaaca-string-utils.cc      | 12 +++++
 5 files changed, 97 insertions(+), 13 deletions(-)

diff --git a/ipaacalib/cpp/include/ipaaca/ipaaca-definitions.h b/ipaacalib/cpp/include/ipaaca/ipaaca-definitions.h
index 681837b..ffdb05c 100644
--- a/ipaacalib/cpp/include/ipaaca/ipaaca-definitions.h
+++ b/ipaacalib/cpp/include/ipaaca/ipaaca-definitions.h
@@ -210,6 +210,14 @@ IPAACA_HEADER_EXPORT class NotImplementedError: public Exception//{{{
 			_description = "NotImplementedError";
 		}
 };//}}}
+IPAACA_HEADER_EXPORT class PayloadTypeConversionError: public Exception//{{{
+{
+	public:
+		IPAACA_HEADER_EXPORT inline ~PayloadTypeConversionError() throw() { }
+		IPAACA_HEADER_EXPORT inline PayloadTypeConversionError() { //boost::shared_ptr<IU> iu) {
+			_description = "PayloadTypeConversionError";
+		}
+};//}}}
 IPAACA_HEADER_EXPORT class PayloadAddressingError: public Exception//{{{
 {
 	public:
@@ -308,6 +316,7 @@ class CommandLineParser {
 //}}}
 // in ipaaca-string-utils.cc
 // additional misc functions ( String splitting / joining )//{{{
+IPAACA_HEADER_EXPORT std::string str_trim(const std::string &s);
 IPAACA_HEADER_EXPORT std::string str_join(const std::set<std::string>& set,const std::string& sep);
 IPAACA_HEADER_EXPORT std::string str_join(const std::vector<std::string>& vec,const std::string& sep);
 IPAACA_HEADER_EXPORT void str_split_wipe(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters );
diff --git a/ipaacalib/cpp/include/ipaaca/ipaaca-payload.h b/ipaacalib/cpp/include/ipaaca/ipaaca-payload.h
index becd6dc..a4d2f26 100644
--- a/ipaacalib/cpp/include/ipaaca/ipaaca-payload.h
+++ b/ipaacalib/cpp/include/ipaaca/ipaaca-payload.h
@@ -44,6 +44,7 @@
 IPAACA_HEADER_EXPORT template<typename T> T json_value_cast(const rapidjson::Value&);
 IPAACA_HEADER_EXPORT template<typename T> T json_value_cast(const rapidjson::Value* value) { if (!value) return T(); return json_value_cast<T>(*value); }
 IPAACA_HEADER_EXPORT template<> long json_value_cast(const rapidjson::Value&);
+IPAACA_HEADER_EXPORT template<> int json_value_cast(const rapidjson::Value&);
 IPAACA_HEADER_EXPORT template<> double json_value_cast(const rapidjson::Value&);
 IPAACA_HEADER_EXPORT template<> bool json_value_cast(const rapidjson::Value&);
 IPAACA_HEADER_EXPORT template<> std::string json_value_cast(const rapidjson::Value&);
@@ -53,6 +54,7 @@ IPAACA_HEADER_EXPORT template<> std::map<std::string, std::string> json_value_ca
 
 // helpers to set Value& from various standard types
 //IPAACA_HEADER_EXPORT template<typename T> void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, T t);
+IPAACA_HEADER_EXPORT void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, int);
 IPAACA_HEADER_EXPORT void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, long);
 IPAACA_HEADER_EXPORT void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, double);
 IPAACA_HEADER_EXPORT void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, bool);
@@ -301,6 +303,26 @@ IPAACA_HEADER_EXPORT class PayloadEntryProxy//{{{
 			_payload->set(_key, new_entry);
 			return *this;
 		}
+		IPAACA_HEADER_EXPORT inline bool operator==(const PayloadEntryProxy& otherproxy) { return (json_value && otherproxy.json_value && ((*json_value)==*(otherproxy.json_value))); }
+		IPAACA_HEADER_EXPORT inline bool operator!=(const PayloadEntryProxy& otherproxy) { return !operator==(otherproxy); }
+		IPAACA_HEADER_EXPORT template<typename T> bool operator==(T othervalue)
+		{
+			if (!json_value) return false;
+			try {
+				return json_value_cast<T>(*json_value) == othervalue;
+			} catch(PayloadTypeConversionError& ex) {
+				// assume conversion error = type mismatch = unequal
+				return false;
+			}
+		}
+		IPAACA_HEADER_EXPORT template<typename T> bool operator!=(T othervalue) { return !operator==(othervalue); }
+		IPAACA_HEADER_EXPORT inline bool operator==(const char* othervalue)
+		{
+			if (!json_value) return false;
+			return json_value_cast<std::string>(*json_value) == othervalue;
+		}
+		IPAACA_HEADER_EXPORT inline bool operator!=(const char* othervalue) { return !operator==(othervalue); }
+		
 		IPAACA_HEADER_EXPORT PayloadEntryProxy& operator=(const PayloadEntryProxy& otherproxy);
 		
 		//IPAACA_HEADER_EXPORT PayloadEntryProxy& operator=(const std::string& value);
diff --git a/ipaacalib/cpp/src/ipaaca-json.cc b/ipaacalib/cpp/src/ipaaca-json.cc
index 943f89e..3be11f6 100644
--- a/ipaacalib/cpp/src/ipaaca-json.cc
+++ b/ipaacalib/cpp/src/ipaaca-json.cc
@@ -47,13 +47,29 @@ int iterators_main(int argc, char** argv)//{{{
 	ipaaca::FakeIU::ptr iu = ipaaca::FakeIU::create();
 	iu->add_fake_payload_item("a", entry);
 	iu->payload()["b"] = "simpleString";
+	iu->payload()["bPrime"] = "simpleString";
 	iu->payload()["c"] = "anotherSimpleString";
+	iu->payload()["d"] = 100;
+	iu->payload()["e"] = 10000l;
 	
 	std::cout << "Iterate over payload" << std::endl;
 	for (auto it = iu->payload().begin(); it != iu->payload().end(); ++it) {
 		std::cout << "  " << it->first << " -> " << it->second << std::endl;
 	}
 	
+	std::cout << "Comparisons" << std::endl;
+	bool eq;
+	eq = iu->payload()["a"] == iu->payload()["b"];
+	std::cout << "  a==b ? : " << (eq?"true":"false") << std::endl;
+	eq = iu->payload()["b"] == iu->payload()["bPrime"];
+	std::cout << "  b==bPrime ? : " << (eq?"true":"false") << std::endl;
+	eq = iu->payload()["b"] == "simpleString";
+	std::cout << "  b==\"simpleString\" ? : " << (eq?"true":"false") << std::endl;
+	eq = iu->payload()["b"] == 100;
+	std::cout << "  b==100 ? : " << (eq?"true":"false") << std::endl;
+	eq = iu->payload()["d"] == 100;
+	std::cout << "  d==100 ? : " << (eq?"true":"false") << std::endl;
+
 	return 0;
 }
 //}}}
diff --git a/ipaacalib/cpp/src/ipaaca-payload.cc b/ipaacalib/cpp/src/ipaaca-payload.cc
index 5989272..e8c6d14 100644
--- a/ipaacalib/cpp/src/ipaaca-payload.cc
+++ b/ipaacalib/cpp/src/ipaaca-payload.cc
@@ -90,6 +90,20 @@ IPAACA_EXPORT std::ostream& operator<<(std::ostream& os, const Payload& obj)//{{
 }
 //}}}
 
+double strict_numerical_interpretation(const std::string& str)
+{
+	char* endptr;
+	auto s = str_trim(str);
+	const char* startptr = s.c_str();
+	long l = strtod(startptr, &endptr);
+	if ((*endptr)=='\0') {
+		// everything could be parsed
+		return l;
+	} else {
+		throw PayloadTypeConversionError();
+	}
+}
+
 // json_value_cast//{{{
 IPAACA_EXPORT template<> std::string json_value_cast(const rapidjson::Value& v)
 {
@@ -102,7 +116,7 @@ IPAACA_EXPORT template<> std::string json_value_cast(const rapidjson::Value& v)
 }
 IPAACA_EXPORT template<> long json_value_cast(const rapidjson::Value& v)
 {
-	if (v.IsString()) return atol(std::string(v.GetString()).c_str());
+	if (v.IsString()) return (long) strict_numerical_interpretation(v.GetString());
 	if (v.IsInt()) return v.GetInt();
 	if (v.IsUint()) return v.GetUint();
 	if (v.IsInt64()) return v.GetInt64();
@@ -111,14 +125,29 @@ IPAACA_EXPORT template<> long json_value_cast(const rapidjson::Value& v)
 	if (v.IsBool()) return v.GetBool() ? 1l : 0l;
 	if (v.IsNull()) return 0l;
 	// default: return parse of string version (should always be 0 though?)
+	throw PayloadTypeConversionError();
+	/*
 	rapidjson::StringBuffer buffer;
 	rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
 	v.Accept(writer);
 	return atol(std::string(buffer.GetString()).c_str());
+	*/
+}
+IPAACA_EXPORT template<> int json_value_cast(const rapidjson::Value& v)
+{
+	if (v.IsString()) return (int) strict_numerical_interpretation(v.GetString());
+	if (v.IsInt()) return v.GetInt();
+	if (v.IsUint()) return v.GetUint();
+	if (v.IsInt64()) return v.GetInt64();
+	if (v.IsUint64()) return v.GetUint64();
+	if (v.IsDouble()) return (long) v.GetDouble();
+	if (v.IsBool()) return v.GetBool() ? 1l : 0l;
+	if (v.IsNull()) return 0l;
+	throw PayloadTypeConversionError();
 }
 IPAACA_EXPORT template<> double json_value_cast(const rapidjson::Value& v)
 {
-	if (v.IsString()) return atof(std::string(v.GetString()).c_str());
+	if (v.IsString()) return strict_numerical_interpretation(v.GetString());
 	if (v.IsDouble()) return v.GetDouble();
 	if (v.IsInt()) return (double) v.GetInt();
 	if (v.IsUint()) return (double) v.GetUint();
@@ -126,11 +155,7 @@ IPAACA_EXPORT template<> double json_value_cast(const rapidjson::Value& v)
 	if (v.IsUint64()) return (double) v.GetUint64();
 	if (v.IsBool()) return v.GetBool() ? 1.0 : 0.0;
 	if (v.IsNull()) return 0.0;
-	// default: return parse of string version (should always be 0.0 though?)
-	rapidjson::StringBuffer buffer;
-	rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
-	v.Accept(writer);
-	return atof(std::string(buffer.GetString()).c_str());
+	throw PayloadTypeConversionError();
 }
 IPAACA_EXPORT template<> bool json_value_cast(const rapidjson::Value& v)
 {
@@ -146,12 +171,8 @@ IPAACA_EXPORT template<> bool json_value_cast(const rapidjson::Value& v)
 	if (v.IsInt64()) return v.GetInt64() != 0;
 	if (v.IsUint64()) return v.GetUint64() != 0;
 	if (v.IsDouble()) return v.GetDouble() != 0.0;
-	// default: return parse of string version (should always be 0.0 though?)
-	rapidjson::StringBuffer buffer;
-	rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
-	v.Accept(writer);
-	std::string s = buffer.GetString();
-	return !((s=="")||(s=="false")||(s=="False")||(s=="0"));
+	// default: assume "pointer-like" semantics (i.e. objects are TRUE)
+	return true;
 }
 /*
  * std::map<std::string, std::string> result;
@@ -161,6 +182,10 @@ IPAACA_EXPORT template<> bool json_value_cast(const rapidjson::Value& v)
 			*/
 //}}}
 
+IPAACA_EXPORT void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, int newvalue)
+{
+	valueobject.SetInt(newvalue);
+}
 IPAACA_EXPORT void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, long newvalue)
 {
 	valueobject.SetInt(newvalue);
diff --git a/ipaacalib/cpp/src/ipaaca-string-utils.cc b/ipaacalib/cpp/src/ipaaca-string-utils.cc
index 992d7cd..d3192fc 100644
--- a/ipaacalib/cpp/src/ipaaca-string-utils.cc
+++ b/ipaacalib/cpp/src/ipaaca-string-utils.cc
@@ -32,8 +32,20 @@
 
 #include <ipaaca/ipaaca.h>
 
+#include <cctype>
+#include <string>
+#include <algorithm>
+
+
 namespace ipaaca {
 
+std::string str_trim(const std::string &s)
+{
+	auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c){ return std::isspace(c); } );
+	auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c){ return std::isspace(c); } ).base();
+	return (wsback<=wsfront ? std::string() : std::string(wsfront, wsback));
+}
+
 std::string str_join(const std::set<std::string>& set,const std::string& sep)
 {
 	if(set.size()==0)
-- 
GitLab