changeset 494:fc17251477d6 bgo-commands-codegen

TS and CPP tests OK. Ongoing code for C++ program that reads list of serialized messages in N files. Requires conan
author bgo-osimis
date Sat, 23 Feb 2019 10:18:13 +0100
parents 6fbf2eae7c88
children 6405435480ae
files .hgignore Resources/CodeGeneration/README.md Resources/CodeGeneration/runts.ps1 Resources/CodeGeneration/stonegentool.py Resources/CodeGeneration/stonegentool_test.py Resources/CodeGeneration/template.in.h Resources/CodeGeneration/template.in.ts Resources/CodeGeneration/testCppHandler/CMakeLists.txt Resources/CodeGeneration/testCppHandler/README.md Resources/CodeGeneration/testCppHandler/conanfile.txt Resources/CodeGeneration/testCppHandler/main.cpp Resources/CodeGeneration/test_data/test1.yaml Resources/CodeGeneration/test_stonegen.html Resources/CodeGeneration/test_stonegen.ts
diffstat 14 files changed, 766 insertions(+), 131 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Fri Feb 22 10:48:43 2019 +0100
+++ b/.hgignore	Sat Feb 23 10:18:13 2019 +0100
@@ -11,3 +11,7 @@
 Resources/CommandTool/flatc-tests/basic/build/
 .vscode/
 Resources/CodeGeneration/__pycache__
+Resources/CodeGeneration/build/
+Resources/CodeGeneration/build_browser/
+Resources/CodeGeneration/testCppHandler/build/
+Resources/CodeGeneration/testCppHandler/build_msbuild/
--- a/Resources/CodeGeneration/README.md	Fri Feb 22 10:48:43 2019 +0100
+++ b/Resources/CodeGeneration/README.md	Sat Feb 23 10:18:13 2019 +0100
@@ -1,24 +1,10 @@
-class TestMessage {
-    s1: string;
-    s2: Array<string>;
-    s3: Array<Array<string>>;
-    s4: Map<string, number>;
-    s5: Map<number, Array<string>>;
-    s6: Color;
-    s7: boolean;
-}
+Requirements
+----------------
 
--->
+Install Node and npm.
 
-{"s2":["toto","toto2","toto3"],"s4":{"toto":1999,"tatata":1999},"s6":0,"s7":true}
-
-(absent fields weren't specified)
-
+Then:
+- `npm install browserify`
+- `npm install typescript`
+- `npm install tsify`
 
-type:B
-value:{"someAs":[{...},{},{}]}.........................
-Deserialize
-
-jsonValue
-
-
--- a/Resources/CodeGeneration/runts.ps1	Fri Feb 22 10:48:43 2019 +0100
+++ b/Resources/CodeGeneration/runts.ps1	Sat Feb 23 10:18:13 2019 +0100
@@ -4,16 +4,60 @@
 
 # tsc -t ES2015 .\template.in.ts; node .\template.in.js
 
-echo "+----------------------+"
-echo "|    playground.ts     |"
-echo "+----------------------+"
+# echo "+----------------------+"
+# echo "|    playground.ts     |"
+# echo "+----------------------+"
+
+# tsc -t ES2015 .\playground.ts; node .\playground.js
 
-tsc -t ES2015 .\playground.ts; node .\playground.js
+# echo "+----------------------+"
+# echo "|    playground3.ts     |"
+# echo "+----------------------+"
+
+# tsc -t ES2015 .\playground3.ts; node .\playground3.js
 
 echo "+----------------------+"
-echo "|    playground3.ts     |"
+echo "|      stonegen        |"
 echo "+----------------------+"
 
-tsc -t ES2015 .\playground3.ts; node .\playground3.js
+if(-not (test-Path "build")) {
+  mkdir "build"
+}
+
+echo "Generate the TS and CPP wrapper... (to build/)"
+python stonegentool.py -o "." test_data/test1.yaml
+if($LASTEXITCODE -ne 0) {
+  Write-Error ("Code generation failed!")
+  exit $LASTEXITCODE
+}
+
+echo "Compile the TS wrapper to JS... (in build/)"
+tsc --module commonjs --sourceMap -t ES2015 --outDir "build/" VsolMessages_generated.ts
+if($LASTEXITCODE -ne 0) {
+  Write-Error ("Code compilation failed!")
+  exit $LASTEXITCODE
+}
+
+echo "Compile the test app..."
+tsc --module commonjs --sourceMap -t ES2015 --outDir "build/" test_stonegen.ts
+if($LASTEXITCODE -ne 0) {
+  Write-Error ("Code compilation failed!")
+  exit $LASTEXITCODE
+}
+
+browserify "build/test_stonegen.js" "build/VsolMessages_generated.js" -o "build_browser/test_stonegen_fused.js"
+
+cp .\test_stonegen.html .\build_browser\
+
+echo "Run the test app..."
+Push-Location
+cd build_browser
+node .\test_stonegen_fused.js
+Pop-Location
+if($LASTEXITCODE -ne 0) {
+  Write-Error ("Code execution failed!")
+  exit $LASTEXITCODE
+}
 
 
+
--- a/Resources/CodeGeneration/stonegentool.py	Fri Feb 22 10:48:43 2019 +0100
+++ b/Resources/CodeGeneration/stonegentool.py	Sat Feb 23 10:18:13 2019 +0100
@@ -1,6 +1,7 @@
 import json
 import yaml
 import re
+import os
 import sys
 from jinja2 import Template
 from typing import (
@@ -117,6 +118,7 @@
   retVal: str = canonicalTypename
   retVal = retVal.replace("map", "std::map")
   retVal = retVal.replace("vector", "std::vector")
+  retVal = retVal.replace("string", "std::string")
   retVal = retVal.replace("int32", "int32_t")
   retVal = retVal.replace("float32", "float")
   retVal = retVal.replace("float64", "double")
@@ -137,9 +139,24 @@
   retVal = retVal.replace("bool", "boolean")
   return retVal
 
-def NeedsConstruction(canonTypename):
+def NeedsTsConstruction(enums: Dict, tsType: str):
+  if tsType == 'boolean':
+    return False
+  elif tsType == 'number':
+    return False
+  elif tsType == 'string':
+    return False
+  else:
+    enumNames = []
+    for enum in enums:
+      enumNames.append(enum['name'])
+    if tsType in enumNames:
+      return False
   return True
 
+def NeedsCppConstruction(canonTypename):
+  return False
+
 def RegisterTemplateFunction(template,func):
   """Makes a function callable by a jinja2 template"""
   template.globals[func.__name__] = func
@@ -149,7 +166,8 @@
   template = Template(templateStr)
   RegisterTemplateFunction(template,CanonToCpp)
   RegisterTemplateFunction(template,CanonToTs)
-  RegisterTemplateFunction(template,NeedsConstruction)
+  RegisterTemplateFunction(template,NeedsTsConstruction)
+  RegisterTemplateFunction(template,NeedsCppConstruction)
   return template
 
 def MakeTemplateFromFile(templateFileName):
@@ -476,12 +494,28 @@
   )
 
   args = parser.parse_args()
-  inputSchemaFilename = args.input_schema
+  schemaFile = args.input_schema
   outDir = args.out_dir
 
-  schema: Dict = LoadSchema(inputSchemaFilename)
-  genOrder: List[str] = ComputeRequiredDeclarationOrder(schema)
-  processedSchema: Dict = ProcessSchema(schema,genOrder)
+  tdico: Dict = GetTemplatingDictFromSchemaFilename(schemaFile)
+
+  tsTemplateFile = \
+    os.path.join(os.path.dirname(__file__), 'template.in.ts')
+  template = MakeTemplateFromFile(tsTemplateFile)
+  renderedTsCode: str = template.render(**tdico)
+  outputTsFile = os.path.join( \
+    outDir,f"{tdico['rootName']}_generated.ts")
+  with open(outputTsFile,"wt",encoding='utf8') as outFile:
+    outFile.write(renderedTsCode)
+
+  cppTemplateFile = \
+    os.path.join(os.path.dirname(__file__), 'template.in.h')
+  template = MakeTemplateFromFile(cppTemplateFile)
+  renderedCppCode: str = template.render(**tdico)
+  outputCppFile = os.path.join( \
+    outDir,f"{tdico['rootName']}_generated.hpp")
+  with open(outputCppFile,"wt",encoding='utf8') as outFile:
+    outFile.write(renderedCppCode)
 
 
 # def GenEnumDecl(genc: GenCode, fullName: str, schema: Dict) -> None:
--- a/Resources/CodeGeneration/stonegentool_test.py	Fri Feb 22 10:48:43 2019 +0100
+++ b/Resources/CodeGeneration/stonegentool_test.py	Sat Feb 23 10:18:13 2019 +0100
@@ -6,7 +6,7 @@
 from stonegentool import \
 EatToken,SplitListOfTypes,ParseTemplateType,ProcessSchema, \
 CheckSchemaSchema,LoadSchema,trim,ComputeRequiredDeclarationOrder, \
-GetTemplatingDictFromSchemaFilename,MakeTemplate
+GetTemplatingDictFromSchemaFilename,MakeTemplate,MakeTemplateFromFile
 import unittest
 import os
 import re
@@ -258,7 +258,7 @@
 {% for key in struct['fields']%}    {{key}}:{{CanonToTs(struct['fields'][key])}};
 {% endfor %}
     constructor() {
-{% for key in struct['fields']%}      {{key}} = new {{CanonToTs(struct['fields'][key])}}();
+{% for key in struct['fields']%}      this.{{key}} = new {{CanonToTs(struct['fields'][key])}}();
 {% endfor %}    }
 
     public StoneSerialize(): string {
@@ -375,12 +375,13 @@
     self.assertEqual(renderedCodeRef, renderedCode)
 
   def test_generateWholeTsFile(self):
-    schemaFile = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
+    schemaFile = \
+      os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
     tdico = GetTemplatingDictFromSchemaFilename(schemaFile)
-    tsTemplateFile = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
+    tsTemplateFile = \
+      os.path.join(os.path.dirname(__file__), 'template.in.ts')
     template = MakeTemplateFromFile(tsTemplateFile)
     renderedCode = template.render(**tdico)
-
     print(renderedCode)
 
   def test_GenerateTypeScriptHandlerInterface(self):
--- a/Resources/CodeGeneration/template.in.h	Fri Feb 22 10:48:43 2019 +0100
+++ b/Resources/CodeGeneration/template.in.h	Sat Feb 23 10:18:13 2019 +0100
@@ -7,38 +7,95 @@
 #include <string>
 #include <sstream>
 #include <assert.h>
+#include <memory>
+#include <optional>
 #include <json/json.h>
+#include <gtest/gtest.h>
 
+//#define STONEGEN_NO_CPP11 1
 
-namespace VsolStuff
+#ifdef STONEGEN_NO_CPP11
+#define StoneSmartPtr std::auto_ptr
+#else 
+#define StoneSmartPtr std::unique_ptr
+#endif 
+
+namespace {{rootName}}
 {
-  Json::Value StoneSerialize(int32_t value)
+  /** Throws in case of problem */
+  void _StoneDeserializeValue(int32_t& destValue, const Json::Value& jsonValue)
+  {
+    destValue = jsonValue.asInt();
+  }
+
+  Json::Value _StoneSerializeValue(int32_t value)
   {
     Json::Value result(value);
     return result;
   }
 
-  Json::Value StoneSerialize(double value)
+  /** Throws in case of problem */
+  void _StoneDeserializeValue(double& destValue, const Json::Value& jsonValue)
+  {
+    destValue = jsonValue.asDouble();
+  }
+
+  Json::Value _StoneSerializeValue(double value)
   {
     Json::Value result(value);
     return result;
   }
 
-  Json::Value StoneSerialize(bool value)
+  /** Throws in case of problem */
+  void _StoneDeserializeValue(bool& destValue, const Json::Value& jsonValue)
+  {
+    destValue = jsonValue.asBool();
+  }
+
+  Json::Value _StoneSerializeValue(bool value)
   {
     Json::Value result(value);
     return result;
   }
 
-  Json::Value StoneSerialize(const std::string& value)
+  /** Throws in case of problem */
+  void _StoneDeserializeValue(
+       std::string& destValue
+     , const Json::Value& jsonValue)
+  {
+    destValue = jsonValue.asString();
+  }
+
+  Json::Value _StoneSerializeValue(const std::string& value)
   {
     // the following is better than 
     Json::Value result(value.data(),value.data()+value.size());
     return result;
   }
     
+  /** Throws in case of problem */
   template<typename T>
-  Json::Value StoneSerialize(const std::map<std::string,T>& value)
+  void _StoneDeserializeValue(
+    std::map<std::string, T>& destValue, const Json::Value& jsonValue)
+  {
+    destValue.clear();
+    for (
+      Json::Value::const_iterator itr = jsonValue.begin();
+      itr != jsonValue.end();
+      itr++)
+    {
+      std::string key;
+      _StoneDeserializeValue(key, itr.key());
+
+      T innerDestValue;
+      _StoneDeserializeValue(innerDestValue, *itr);
+
+      destValue[key] = innerDestValue;
+    }
+  }
+
+  template<typename T>
+  Json::Value _StoneSerializeValue(const std::map<std::string,T>& value)
   {
     Json::Value result(Json::objectValue);
 
@@ -46,24 +103,190 @@
       it != value.cend(); ++it)
     {
       // it->first it->second
-      result[it->first] = StoneSerialize(it->second);
+      result[it->first] = _StoneSerializeValue(it->second);
     }
     return result;
   }
 
+  /** Throws in case of problem */
   template<typename T>
-  Json::Value StoneSerialize(const std::vector<T>& value)
+  void _StoneDeserializeValue(
+    std::vector<T>& destValue, const Json::Value& jsonValue)
+  {
+    destValue.clear();
+    destValue.reserve(jsonValue.size());
+    for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++)
+    {
+      T innerDestValue;
+      _StoneDeserializeValue(innerDestValue, jsonValue[i]);
+      destValue.push_back(innerDestValue);
+    }
+  }
+
+  template<typename T>
+  Json::Value _StoneSerializeValue(const std::vector<T>& value)
   {
     Json::Value result(Json::arrayValue);
     for (size_t i = 0; i < value.size(); ++i)
     {
-      result.append(StoneSerialize(value[i]));
+      result.append(_StoneSerializeValue(value[i]));
     }
     return result;
   }
 
-  %enumerationscpp%
+  void StoneCheckSerializedValueTypeGeneric(const Json::Value& value)
+  {
+    if ((!value.isMember("type")) || (!value["type"].isString()))
+    {
+      std::stringstream ss;
+      ss << "Cannot deserialize value ('type' key invalid)";
+      throw std::runtime_error(ss.str());
+    }
+  }
+
+  void StoneCheckSerializedValueType(
+    const Json::Value& value, std::string typeStr)
+  {
+    StoneCheckSerializedValueTypeGeneric(value);
+
+    std::string actTypeStr = value["type"].asString();
+    if (actTypeStr != typeStr)
+    {
+      std::stringstream ss;
+      ss << "Cannot deserialize type" << actTypeStr
+        << "into " << typeStr;
+      throw std::runtime_error(ss.str());
+    }
+  }
+
+  // end of generic methods
+
+// end of generic methods
+{% for enum in enums%}
+  enum {{enum['name']}} {
+{% for key in enum['fields']%}    {{key}},
+{%endfor%}  };
+
+  void _StoneDeserializeValue(
+    {{enum['name']}}& destValue, const Json::Value& jsonValue)
+  {
+    destValue = static_cast<{{enum['name']}}>(jsonValue.asInt64());
+  }
+
+  Json::Value _StoneSerializeValue(const {{enum['name']}}& value)
+  {
+    return Json::Value(static_cast<int64_t>(value));
+  }
+{%endfor%}
+{% for struct in structs%}
+#ifdef _MSC_VER
+#pragma region {{struct['name']}}
+#endif //_MSC_VER
+
+  struct {{struct['name']}}
+  {
+{% for key in struct['fields']%}    {{CanonToCpp(struct['fields'][key])}} {{key}};
+{% endfor %}
+    {{struct['name']}}()
+    {
+{% for key in struct['fields']%}      {{key}} = {{CanonToCpp(struct['fields'][key])}}();
+{% endfor %}
+    }
+  };
+
+  void _StoneDeserializeValue({{struct['name']}}& destValue, const Json::Value& value)
+  {
+{% for key in struct['fields']%}    _StoneDeserializeValue(destValue.{{key}}, value["{{key}}"]);
+{% endfor %}
+  }
+
+  Json::Value _StoneSerializeValue(const {{struct['name']}}& value)
+  {
+    Json::Value result(Json::objectValue);
+{% for key in struct['fields']%}    result["{{key}}"] = _StoneSerializeValue(value.{{key}});
+{% endfor %}
+    return result;
+  }
 
-  %structscpp%
+  void StoneDeserialize({{struct['name']}}& destValue, const Json::Value& value)
+  {
+    StoneCheckSerializedValueType(value, "{{rootName}}.{{struct['name']}}");
+    _StoneDeserializeValue(destValue, value["value"]);
+  }
+
+  Json::Value StoneSerialize(const {{struct['name']}}& value)
+  {
+    Json::Value result(Json::objectValue);
+    result["type"] = "{{rootName}}.{{struct['name']}}";
+    result["value"] = _StoneSerializeValue(value);
+    return result;
+  }
+
+#ifdef _MSC_VER
+#pragma endregion {{struct['name']}}
+#endif //_MSC_VER
+{% endfor %}
+#ifdef _MSC_VER
+#pragma region Dispatching code
+#endif //_MSC_VER
+
+  class IDispatcher
+  {
+  public:
+{% for struct in structs%}    virtual bool Handle(const {{struct['name']}}& value) = 0;
+{% endfor %}  };
 
+  /** Service function for StoneDispatchToHandler */
+  bool StoneDispatchJsonToHandler(
+    const Json::Value& jsonValue, IDispatcher* dispatcher)
+  {
+    StoneCheckSerializedValueTypeGeneric(jsonValue);
+    std::string type = jsonValue["type"].asString();
+    if (type == "")
+    {
+      // this should never ever happen
+      throw std::runtime_error("Caught empty type while dispatching");
+    }
+{% for struct in structs%}    else if (type == "{{rootName}}.{{struct['name']}}")
+    {
+      {{struct['name']}} value;
+      _StoneDeserializeValue(value, jsonValue["value"]);
+      return dispatcher->Handle(value);
+    }
+{% endfor %}    else
+    {
+      return false;
+    }
+  }
+
+  /** Takes a serialized type and passes this to the dispatcher */
+  bool StoneDispatchToHandler(std::string strValue, IDispatcher* dispatcher)
+  {
+    Json::Value readValue;
+
+    Json::CharReaderBuilder builder;
+    Json::CharReader* reader = builder.newCharReader();
+
+    StoneSmartPtr<Json::CharReader> ptr(reader);
+
+    std::string errors;
+
+    bool ok = reader->parse(
+      strValue.c_str(),
+      strValue.c_str() + strValue.size(),
+      &readValue,
+      &errors
+    );
+    if (!ok)
+    {
+      std::stringstream ss;
+      ss << "Jsoncpp parsing error: " << errors;
+      throw std::runtime_error(ss.str());
+    }
+    return StoneDispatchJsonToHandler(readValue, dispatcher);
+  }
+
+#ifdef _MSC_VER
+#pragma endregion Dispatching code
+#endif //_MSC_VER
 }
--- a/Resources/CodeGeneration/template.in.ts	Fri Feb 22 10:48:43 2019 +0100
+++ b/Resources/CodeGeneration/template.in.ts	Sat Feb 23 10:18:13 2019 +0100
@@ -3,107 +3,106 @@
 12345678901234567890123456789012345678901234567890123456789012345678901234567890
 */
 
-namespace {{rootName}}
+function StoneCheckSerializedValueType(value: any, typeStr: string)
 {
-  function StoneCheckSerializedValueType(value: any, typeStr: string)
-  {
-    StoneCheckSerializedValueTypeGeneric(value);
+  StoneCheckSerializedValueTypeGeneric(value);
 
-    if (value['type'] != typeStr)
-    {
-      throw new Error(
-        `Cannot deserialize type ${value['type']} into ${typeStr}`);
-    }
+  if (value['type'] != typeStr)
+  {
+    throw new Error(
+      `Cannot deserialize type ${value['type']} into ${typeStr}`);
   }
+}
 
-  function isString(val: any) :boolean
-  {
-    return ((typeof val === 'string') || (val instanceof String));
-  }
-  
-  function StoneCheckSerializedValueTypeGeneric(value: any)
+function isString(val: any) :boolean
+{
+  return ((typeof val === 'string') || (val instanceof String));
+}
+
+function StoneCheckSerializedValueTypeGeneric(value: any)
+{
+  // console.//log("+-------------------------------------------------+");
+  // console.//log("|            StoneCheckSerializedValueTypeGeneric |");
+  // console.//log("+-------------------------------------------------+");
+  // console.//log("value = ");
+  // console.//log(value);
+  if ( (!('type' in value)) || (!isString(value.type)) )
   {
-    if ( (!('type' in value)) || (!isString(value)) )
-    {
-      throw new Error(
-        "Cannot deserialize value ('type' key invalid)");
-    }
+    throw new Error(
+      "Cannot deserialize value ('type' key invalid)");
   }
+}
 
 // end of generic methods
 {% for enum in enums%}
-  export enum {{enum['name']}} {
-    {% for key in enumDict.keys()%}
-    {{key}},
-    {%endfor%}
-  };
+export enum {{enum['name']}} {
+  {% for key in enum['fields']%}{{key}},
+  {%endfor%}
+};
 {%endfor%}
 
-
-"""  // end of generic methods
 {% for struct in structs%}  export class {{struct['name']}} {
 {% for key in struct['fields']%}    {{key}}:{{CanonToTs(struct['fields'][key])}};
 {% endfor %}
-    constructor() {
-{% for key in struct['fields']%}      {{key}} = new {{CanonToTs(struct['fields'][key])}}();
-{% endfor %}    }
+  constructor() {
+{% for key in struct['fields']%}{% if NeedsTsConstruction(enums,CanonToTs(struct['fields'][key])) %}      this.{{key}} = new {{CanonToTs(struct['fields'][key])}}();
+{% endif %}{% endfor %}    }
 
-    public StoneSerialize(): string {
-      let container: object = {};
-      container['type'] = '{{rWholootName}}.{{struct['name']}}';
-      container['value'] = this;
-      return JSON.stringify(container);
-    }
+  public StoneSerialize(): string {
+    let container: object = {};
+    container['type'] = '{{rWholootName}}.{{struct['name']}}';
+    container['value'] = this;
+    return JSON.stringify(container);
+  }
 
-    public static StoneDeserialize(valueStr: string) : {{struct['name']}}
-    {
-      let value: any = JSON.parse(valueStr);
-      StoneCheckSerializedValueType(value, '{{rootName}}.{{struct['name']}}');
-      let result: {{struct['name']}} = value['value'] as {{struct['name']}};
-      return result;
-    }
-
+  public static StoneDeserialize(valueStr: string) : {{struct['name']}}
+  {
+    let value: any = JSON.parse(valueStr);
+    StoneCheckSerializedValueType(value, '{{rootName}}.{{struct['name']}}');
+    let result: {{struct['name']}} = value['value'] as {{struct['name']}};
+    return result;
   }
+}
 
 {% endfor %}
 
-    };
+export interface IDispatcher {
+  {% for struct in structs%}    Handle{{struct['name']}}(value:  {{struct['name']}}): boolean;
+  {% endfor %}
+};
 
-  export interface IDispatcher
-  {
-    {% for struct in structs%}    HandleMessage1(value:  {{struct['name']}}): boolean;
-    {% endfor %}
-  };
-
-  /** Service function for StoneDispatchToHandler */
-  export function StoneDispatchJsonToHandler(
-    jsonValueStr: string, dispatcher: IDispatcher): boolean
+/** Service function for StoneDispatchToHandler */
+export function StoneDispatchJsonToHandler(
+  jsonValue: any, dispatcher: IDispatcher): boolean
+{
+  StoneCheckSerializedValueTypeGeneric(jsonValue);
+  let type: string = jsonValue["type"];
+  if (type == "")
   {
-    let jsonValue: any = JSON.parse(jsonValueStr);
-    StoneCheckSerializedValueTypeGeneric(jsonValue);
-    let type: string = jsonValue["type"];
-    if (type == "")
-    {
-      // this should never ever happen
-      throw new Error("Caught empty type while dispatching");
-    }
-{% for struct in structs%}    else if (type == "VsolStuff.{{struct['name']}}")
-    {
-      let value = jsonValue["value"] as Message1;
-      return dispatcher.HandleMessage1(value);
-    }
-{% enfor %}
-    else
-    {
-      return false;
-    }
+    // this should never ever happen
+    throw new Error("Caught empty type while dispatching");
   }
-
-  /** Takes a serialized type and passes this to the dispatcher */
-  export function StoneDispatchToHandler(
-    strValue: string, dispatcher: IDispatcher): boolean
+{% for struct in structs%}    else if (type == "{{rootName}}.{{struct['name']}}")
   {
-    let jsonValue: any = JSON.parse(strValue)
-    return StoneDispatchJsonToHandler(jsonValue, dispatcher);
+    let value = jsonValue["value"] as {{struct['name']}};
+    return dispatcher.Handle{{struct['name']}}(value);
+  }
+{% endfor %}
+  else
+  {
+    return false;
   }
 }
+
+/** Takes a serialized type and passes this to the dispatcher */
+export function StoneDispatchToHandler(
+  strValue: string, dispatcher: IDispatcher): boolean
+{
+  // console.//log("+------------------------------------------------+");
+  // console.//log("|            StoneDispatchToHandler              |");
+  // console.//log("+------------------------------------------------+");
+  // console.//log("strValue = ");
+  // console.//log(strValue);
+  let jsonValue: any = JSON.parse(strValue)
+  return StoneDispatchJsonToHandler(jsonValue, dispatcher);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CodeGeneration/testCppHandler/CMakeLists.txt	Sat Feb 23 10:18:13 2019 +0100
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(testCppHandler)
+
+add_custom_command(
+    OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/VsolMessages_generated.hpp
+    COMMAND python ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/test1.yaml
+    DEPENDS ${CMAKE_CURRENT_LIST_DIR}/../test_data/test1.yaml
+)
+
+include(${CMAKE_BINARY_DIR}/conanbuildinfo_multi.cmake)
+conan_basic_setup()
+
+add_executable(testCppHandler main.cpp ${CMAKE_CURRENT_BINARY_DIR}/VsolMessages_generated.hpp)
+
+target_include_directories(testCppHandler PUBLIC ${CMAKE_BINARY_DIR})
+
+conan_target_link_libraries(testCppHandler)
+
+set_property(TARGET testCppHandler PROPERTY CXX_STANDARD 17)
+
+install(TARGETS testCppHandler DESTINATION bin)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CodeGeneration/testCppHandler/README.md	Sat Feb 23 10:18:13 2019 +0100
@@ -0,0 +1,32 @@
+Requirements
+==============
+- Install Python 3.x (tested with 3.7)
+- Install conan with `pip install conan` (tested with 1.12.2)
+- Install CMake (tested with 3.12)
+- Under Windows: Visual Studio 2017
+- Under *nix*: Ninja
+
+How to build under *nix*
+===============================
+- Navigate to `testCppHandler` folder
+- `conan install . -g cmake`
+- `mkdir build`
+- `cd build`
+- `cmake -G "Ninja" ..`
+- `cmake --build . --config Debug` or - `cmake --build . --config Release`
+
+How to build under Windows with Visual Studio
+==============================================
+- Navigate to repo root
+- `mkdir build`
+- `cd build`
+- `conan install .. -g cmake_multi -s build_type=Release`
+- `conan install .. -g cmake_multi -s build_type=Debug`
+- `cmake -G "Visual Studio 15 2017 Win64" ..`  (modify for your current Visual Studio version)
+- `cmake --build . --config Debug` or - `cmake --build . --config Release`
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CodeGeneration/testCppHandler/conanfile.txt	Sat Feb 23 10:18:13 2019 +0100
@@ -0,0 +1,4 @@
+[requires]
+jsoncpp/1.8.4@theirix/stable
+gtest/1.8.1@bincrafters/stable
+boost/1.69.0@conan/stable
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CodeGeneration/testCppHandler/main.cpp	Sat Feb 23 10:18:13 2019 +0100
@@ -0,0 +1,72 @@
+#include <string>
+#include <filesystem>
+#include <regex>
+using namespace std;
+namespace fs = std::filesystem;
+
+#include <boost/program_options.hpp>
+using namespace boost::program_options;
+
+#include "VsolMessages_generated.hpp"
+
+/**
+Transforms `str` by replacing occurrences of `oldStr` with `newStr`, using 
+plain text (*not* regular expressions.)
+*/
+static inline void ReplaceInString(
+  string& str,
+  const std::string& oldStr,
+  const std::string& newStr)
+{
+  std::string::size_type pos = 0u;
+  while ((pos = str.find(oldStr, pos)) != std::string::npos) {
+    str.replace(pos, oldStr.length(), newStr);
+    pos += newStr.length();
+  }
+}
+
+int main(int argc, char** argv)
+{
+  try
+  {
+    string pattern;
+
+    options_description desc("Allowed options");
+    desc.add_options()
+      // First parameter describes option name/short name
+      // The second is parameter to option
+      // The third is description
+      ("help,h", "print usage message")
+      ("pattern,p", value(&pattern), "pattern for input")
+      ;
+
+    variables_map vm;
+    store(parse_command_line(argc, argv, desc), vm);
+
+    if (vm.count("help"))
+    {
+      cout << desc << "\n";
+      return 0;
+    }
+
+    // tranform globbing pattern into regex
+    // we should deal with -, ., *...
+    string regexPatternStr = pattern;
+    regex regexPattern(regexPatternStr);
+
+    for (auto& p : fs::directory_iterator("."))
+    {
+      if (regex_match(p.path().string(), regexPattern))
+        std::cout << "\"" << p << "\" is a match\n";
+      else
+        std::cout << "\"" << p << "\" is *not* a match\n";
+    }
+    return 0;
+
+
+  }
+  catch (exception& e)
+  {
+    cerr << e.what() << "\n";
+  }
+}
\ No newline at end of file
--- a/Resources/CodeGeneration/test_data/test1.yaml	Fri Feb 22 10:48:43 2019 +0100
+++ b/Resources/CodeGeneration/test_data/test1.yaml	Sat Feb 23 10:18:13 2019 +0100
@@ -29,6 +29,7 @@
   tutu: vector<string>
   titi: map<string, string>
   lulu: map<string, Message1>
+  movieType: MovieType
 
 enum MovieType:
   - RomCom
@@ -37,7 +38,12 @@
   - Vegetables
 
 enum CrispType:
- - SaltAndPepper
- - CreamAndChives
- - Paprika
- - Barbecue
+  - SaltAndPepper
+  - CreamAndChives
+  - Paprika
+  - Barbecue
+
+enum EnumMonth0:
+  - January
+  - February
+  - March
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CodeGeneration/test_stonegen.html	Sat Feb 23 10:18:13 2019 +0100
@@ -0,0 +1,1 @@
+<script type="text/javascript" src="test_stonegen_fused.js"></script>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CodeGeneration/test_stonegen.ts	Sat Feb 23 10:18:13 2019 +0100
@@ -0,0 +1,206 @@
+import * as VsolMessages from "./VsolMessages_generated";
+
+function TEST_StoneGen_SerializeComplex() {
+  let msg1_0 = new VsolMessages.Message1();
+  msg1_0.a = 42;
+  msg1_0.b = "Benjamin";
+  msg1_0.c = VsolMessages.EnumMonth0.January;
+  msg1_0.d = true;
+  let msg1_1 = new VsolMessages.Message1();
+  msg1_1.a = 43;
+  msg1_1.b = "Sandrine";
+  msg1_1.c = VsolMessages.EnumMonth0.March;
+  msg1_0.d = false;
+  let result1_0 = msg1_0.StoneSerialize();
+  let resultStr1_0 = JSON.stringify(result1_0);
+  let result1_1 = msg1_1.StoneSerialize();
+  let resultStr1_1 = JSON.stringify(result1_1);
+  // std::string toto;
+  // std::vector<Message1> tata;
+  // std::vector<std::string> tutu;
+  // std::map<int32_t, std::string> titi;
+  // std::map<int32_t, Message1> lulu;
+  let msg2_0 = new VsolMessages.Message2();
+  msg2_0.toto = "Prout zizi";
+  msg2_0.tata.push(msg1_0);
+  msg2_0.tata.push(msg1_1);
+  msg2_0.tutu.push("Mercadet");
+  msg2_0.tutu.push("Poisson");
+  msg2_0.titi["44"] = "key 44";
+  msg2_0.titi["45"] = "key 45";
+  msg2_0.lulu["54"] = msg1_1;
+  msg2_0.lulu["55"] = msg1_0;
+  let result2 = msg2_0.StoneSerialize();
+  let resultStr2 = JSON.stringify(result2);
+  let refResult2 = `{
+"type" : "VsolMessages.Message2",
+"value" : 
+{
+  "lulu" : 
+  {
+    "54" : 
+    {
+      "a" : 43,
+      "b" : "Sandrine",
+      "c" : 2,
+      "d" : true
+    },
+    "55" : 
+    {
+      "a" : 42,
+      "b" : "Benjamin",
+      "c" : 0,
+      "d" : false
+    }
+  },
+  "tata" : 
+  [
+    {
+      "a" : 42,
+      "b" : "Benjamin",
+      "c" : 0,
+      "d" : false
+    },
+    {
+      "a" : 43,
+      "b" : "Sandrine",
+      "c" : 2,
+      "d" : true
+    }
+  ],
+  "titi" : 
+  {
+    "44" : "key 44",
+    "45" : "key 45"
+  },
+  "toto" : "Prout zizi",
+  "tutu" : 
+  [
+    "Mercadet",
+    "Poisson"
+  ]
+}
+}
+`;
+  let refResult2Obj = JSON.parse(refResult2);
+  let resultStr2Obj = JSON.parse(resultStr2);
+  if (false) {
+    if (refResult2Obj !== resultStr2Obj) {
+      console.log("Results are different!");
+      console.log(`refResult2Obj['value']['lulu']['54'] = ${refResult2Obj['value']['lulu']['54']}`);
+      console.log(`refResult2Obj['value']['lulu']['54']['a'] = ${refResult2Obj['value']['lulu']['54']['a']}`);
+      console.log("************************************************************");
+      console.log("**                  REFERENCE OBJ                         **");
+      console.log("************************************************************");
+      console.log(refResult2Obj);
+      console.log("************************************************************");
+      console.log("**                  ACTUAL OBJ                            **");
+      console.log("************************************************************");
+      console.log(resultStr2Obj);
+      console.log("************************************************************");
+      console.log("**                  REFERENCE                             **");
+      console.log("************************************************************");
+      console.log(refResult2);
+      console.log("************************************************************");
+      console.log("**                  ACTUAL                                **");
+      console.log("************************************************************");
+      console.log(resultStr2);
+      throw new Error("Wrong serialization");
+    }
+  }
+  let refResultValue = JSON.parse(resultStr2);
+  console.log(refResultValue);
+}
+class MyDispatcher {
+  message1: VsolMessages.Message1;
+  message2: VsolMessages.Message2;
+
+  HandleMessage1(value: VsolMessages.Message1) {
+    this.message1 = value;
+    return true;
+  }
+  HandleMessage2(value: VsolMessages.Message2) {
+    this.message2 = value;
+    return true;
+  }
+  HandleA(value) {
+    return true;
+  }
+  HandleB(value) {
+    return true;
+  }
+  HandleC(value) {
+    return true;
+  }
+}
+;
+function TEST_StoneGen_DeserializeOkAndNok() {
+  let serializedMessage = `{
+"type" : "VsolMessages.Message2",
+"value" : 
+{
+  "lulu" : 
+  {
+    "54" : 
+    {
+      "a" : 43,
+      "b" : "Sandrine",
+      "c" : 2,
+      "d" : true
+    },
+    "55" : 
+    {
+      "a" : 42,
+      "b" : "Benjamin",
+      "c" : 0,
+      "d" : false
+    }
+  },
+  "tata" : 
+  [
+    {
+      "a" : 42,
+      "b" : "Benjamin",
+      "c" : 0,
+      "d" : false
+    },
+    {
+      "a" : 43,
+      "b" : "Sandrine",
+      "c" : 2,
+      "d" : true
+    }
+  ],
+  "titi" : 
+  {
+    "44" : "key 44",
+    "45" : "key 45"
+  },
+  "toto" : "Prout zizi",
+  "tutu" : 
+  [
+    "Mercadet",
+    "Poisson"
+  ]
+}
+}`;
+  let myDispatcher = new MyDispatcher();
+  let ok = VsolMessages.StoneDispatchToHandler(serializedMessage, myDispatcher);
+  if (!ok) {
+    throw Error("Error when dispatching message!");
+  }
+  if (myDispatcher.message1 != undefined) {
+    throw Error("(myDispatcher.Message1 != undefined)");
+  }
+  if (myDispatcher.message2 == undefined) {
+    throw Error("(myDispatcher.Message2 == undefined)");
+  }
+  console.log("TEST_StoneGen_DeserializeOkAndNok: OK!");
+}
+function main() {
+  console.log("Entering main()");
+  TEST_StoneGen_SerializeComplex();
+  TEST_StoneGen_DeserializeOkAndNok();
+  return 0;
+}
+console.log(`Exit code is: ${main()}`);
\ No newline at end of file