From 9747a8d5ce3ed707556ebae38eb79b499d10c769 Mon Sep 17 00:00:00 2001 From: Christopher Warrington Date: Mon, 22 Aug 2016 18:23:26 -0700 Subject: [PATCH] [c++] Generate FromEnum for string conversions * Generated enum types now have a `FromEnum` method that can be used to convert from an enum value to a string. Now generated enum types have all four of `ToEnum`, `FromEnum`, `ToString`, and `FromString`. (The `...Enum` variants return false on failure, while the `...String` variants throw.) * enumerations example updated to demonstrate all four of these functions. * Existing throwing `FromString` re-implemented in terms of non-throwing `ToEnum`. * `ToString` is intentionally not implemented in terms of `FromEnum`, as `ToString` returns a reference to the name stored in the map. `FromEnum` copies this name into the output paramater. Fixes https://github.com/Microsoft/bond/pull/223 --- CHANGELOG.md | 15 ++++ .../Language/Bond/Codegen/Cpp/Types_cpp.hs | 12 ++- .../src/Language/Bond/Codegen/Cpp/Types_h.hs | 14 +++ .../generated/allocator/attributes_types.cpp | 8 +- .../generated/allocator/attributes_types.h | 14 +++ .../generated/allocator/defaults_types.cpp | 8 +- .../generated/allocator/defaults_types.h | 14 +++ compiler/tests/generated/attributes_types.cpp | 8 +- compiler/tests/generated/attributes_types.h | 14 +++ compiler/tests/generated/defaults_types.cpp | 8 +- compiler/tests/generated/defaults_types.h | 14 +++ .../cpp/core/enumerations/enumerations.cpp | 85 ++++++++++++++++++- 12 files changed, 177 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03c0891e..0274735b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,21 @@ tag versions. The Bond compiler (`gbc`) and different versioning scheme, following the Haskell community's [package versioning policy](https://wiki.haskell.org/Package_versioning_policy). +## Unreleased ## + +* `gbc` & compiler library: minor bump needed +* IDL core version: TBD +* IDL comm version: TBD +* C++ version: minor bump needed +* C# NuGet version: TBD +* C# Comm NuGet version: TBD + +### C++ ### +* Generated enum types now have a `FromEnum` method that can be used to + convert from an enum value to a string. Now generated enum types have all + four of `ToEnum`, `FromEnum`, `ToString`, and `FromString`. (The `...Enum` + variants return false on failure, while the `...String` variants throw.) + ## 4.3.0: 2016-08-23 ## * `gbc` & compiler library: 0.5.0.0 diff --git a/compiler/src/Language/Bond/Codegen/Cpp/Types_cpp.hs b/compiler/src/Language/Bond/Codegen/Cpp/Types_cpp.hs index 2aad202d..b5c6b44c 100644 --- a/compiler/src/Language/Bond/Codegen/Cpp/Types_cpp.hs +++ b/compiler/src/Language/Bond/Codegen/Cpp/Types_cpp.hs @@ -31,6 +31,10 @@ types_cpp cpp file _imports declarations = ("_types.cpp", [lt| if null declParams then CPP.schemaMetadata cpp s else mempty -- global variables for enum name/value conversions + -- + -- ToString is intentionally not implemented in terms of FromEnum, as + -- ToString returns a reference to the name stored in the map. FromEnum + -- copies this name into the output paramater. statics Enum {..} = [lt| namespace _bond_enumerators { @@ -58,15 +62,9 @@ types_cpp cpp file _imports declarations = ("_types.cpp", [lt| void FromString(const std::string& name, enum #{declName}& value) { - std::map::const_iterator it = - _name_to_value_#{declName}.find(name); - - if (_name_to_value_#{declName}.end() == it) + if (!ToEnum(value, name)) bond::InvalidEnumValueException(name.c_str(), "#{declName}"); - - value = it->second; } - } // namespace #{declName} } // namespace _bond_enumerators|] where diff --git a/compiler/src/Language/Bond/Codegen/Cpp/Types_h.hs b/compiler/src/Language/Bond/Codegen/Cpp/Types_h.hs index f8dc516f..a1a4d56d 100644 --- a/compiler/src/Language/Bond/Codegen/Cpp/Types_h.hs +++ b/compiler/src/Language/Bond/Codegen/Cpp/Types_h.hs @@ -373,6 +373,20 @@ namespace std return true; } + + inline + bool FromEnum(std::string& name, enum #{declName} value) + { + std::map::const_iterator it = + _value_to_name_#{declName}.find(value); + + if (_value_to_name_#{declName}.end() == it) + return false; + + name = it->second; + + return true; + } } // namespace #{declName} } // namespace _bond_enumerators diff --git a/compiler/tests/generated/allocator/attributes_types.cpp b/compiler/tests/generated/allocator/attributes_types.cpp index 8c1236e2..2d7cc895 100644 --- a/compiler/tests/generated/allocator/attributes_types.cpp +++ b/compiler/tests/generated/allocator/attributes_types.cpp @@ -31,15 +31,9 @@ namespace tests void FromString(const std::string& name, enum Enum& value) { - std::map::const_iterator it = - _name_to_value_Enum.find(name); - - if (_name_to_value_Enum.end() == it) + if (!ToEnum(value, name)) bond::InvalidEnumValueException(name.c_str(), "Enum"); - - value = it->second; } - } // namespace Enum } // namespace _bond_enumerators diff --git a/compiler/tests/generated/allocator/attributes_types.h b/compiler/tests/generated/allocator/attributes_types.h index ceeb65ee..9e623dc1 100644 --- a/compiler/tests/generated/allocator/attributes_types.h +++ b/compiler/tests/generated/allocator/attributes_types.h @@ -74,6 +74,20 @@ namespace tests return true; } + + inline + bool FromEnum(std::string& name, enum Enum value) + { + std::map::const_iterator it = + _value_to_name_Enum.find(value); + + if (_value_to_name_Enum.end() == it) + return false; + + name = it->second; + + return true; + } } // namespace Enum } // namespace _bond_enumerators diff --git a/compiler/tests/generated/allocator/defaults_types.cpp b/compiler/tests/generated/allocator/defaults_types.cpp index 5e07a294..447a47d3 100644 --- a/compiler/tests/generated/allocator/defaults_types.cpp +++ b/compiler/tests/generated/allocator/defaults_types.cpp @@ -41,15 +41,9 @@ namespace tests void FromString(const std::string& name, enum EnumType1& value) { - std::map::const_iterator it = - _name_to_value_EnumType1.find(name); - - if (_name_to_value_EnumType1.end() == it) + if (!ToEnum(value, name)) bond::InvalidEnumValueException(name.c_str(), "EnumType1"); - - value = it->second; } - } // namespace EnumType1 } // namespace _bond_enumerators diff --git a/compiler/tests/generated/allocator/defaults_types.h b/compiler/tests/generated/allocator/defaults_types.h index 392d6065..d012b318 100644 --- a/compiler/tests/generated/allocator/defaults_types.h +++ b/compiler/tests/generated/allocator/defaults_types.h @@ -84,6 +84,20 @@ namespace tests return true; } + + inline + bool FromEnum(std::string& name, enum EnumType1 value) + { + std::map::const_iterator it = + _value_to_name_EnumType1.find(value); + + if (_value_to_name_EnumType1.end() == it) + return false; + + name = it->second; + + return true; + } } // namespace EnumType1 } // namespace _bond_enumerators diff --git a/compiler/tests/generated/attributes_types.cpp b/compiler/tests/generated/attributes_types.cpp index 8c1236e2..2d7cc895 100644 --- a/compiler/tests/generated/attributes_types.cpp +++ b/compiler/tests/generated/attributes_types.cpp @@ -31,15 +31,9 @@ namespace tests void FromString(const std::string& name, enum Enum& value) { - std::map::const_iterator it = - _name_to_value_Enum.find(name); - - if (_name_to_value_Enum.end() == it) + if (!ToEnum(value, name)) bond::InvalidEnumValueException(name.c_str(), "Enum"); - - value = it->second; } - } // namespace Enum } // namespace _bond_enumerators diff --git a/compiler/tests/generated/attributes_types.h b/compiler/tests/generated/attributes_types.h index 47b2ed66..f8943f0d 100644 --- a/compiler/tests/generated/attributes_types.h +++ b/compiler/tests/generated/attributes_types.h @@ -74,6 +74,20 @@ namespace tests return true; } + + inline + bool FromEnum(std::string& name, enum Enum value) + { + std::map::const_iterator it = + _value_to_name_Enum.find(value); + + if (_value_to_name_Enum.end() == it) + return false; + + name = it->second; + + return true; + } } // namespace Enum } // namespace _bond_enumerators diff --git a/compiler/tests/generated/defaults_types.cpp b/compiler/tests/generated/defaults_types.cpp index 5e07a294..447a47d3 100644 --- a/compiler/tests/generated/defaults_types.cpp +++ b/compiler/tests/generated/defaults_types.cpp @@ -41,15 +41,9 @@ namespace tests void FromString(const std::string& name, enum EnumType1& value) { - std::map::const_iterator it = - _name_to_value_EnumType1.find(name); - - if (_name_to_value_EnumType1.end() == it) + if (!ToEnum(value, name)) bond::InvalidEnumValueException(name.c_str(), "EnumType1"); - - value = it->second; } - } // namespace EnumType1 } // namespace _bond_enumerators diff --git a/compiler/tests/generated/defaults_types.h b/compiler/tests/generated/defaults_types.h index 0bb3ddae..b7bba62c 100644 --- a/compiler/tests/generated/defaults_types.h +++ b/compiler/tests/generated/defaults_types.h @@ -84,6 +84,20 @@ namespace tests return true; } + + inline + bool FromEnum(std::string& name, enum EnumType1 value) + { + std::map::const_iterator it = + _value_to_name_EnumType1.find(value); + + if (_value_to_name_EnumType1.end() == it) + return false; + + name = it->second; + + return true; + } } // namespace EnumType1 } // namespace _bond_enumerators diff --git a/examples/cpp/core/enumerations/enumerations.cpp b/examples/cpp/core/enumerations/enumerations.cpp index 33833af5..0f2fb413 100644 --- a/examples/cpp/core/enumerations/enumerations.cpp +++ b/examples/cpp/core/enumerations/enumerations.cpp @@ -1,13 +1,34 @@ #include #include #include +#include #include "enumerations_enum.h" +#include "enumerations_types.h" using namespace examples::enumerations; +static void DisambiguateEnumsWithSameName(); +static void EnumValueLimits(); +static void ConversionToFromEnum(); +static void ConversionsToFromString(); + int main() { + DisambiguateEnumsWithSameName(); + EnumValueLimits(); + ConversionToFromEnum(); + ConversionsToFromString(); + + return 0; +} + +// Bond provides a standard-compliant solution for scoped enumerations in +// C++ that overcomes the limitations of normal C++ enumeration types. +void DisambiguateEnumsWithSameName() +{ + // Both Color and Fruit have a value named Orange. + Color color; color = Color::Orange; @@ -17,11 +38,71 @@ int main() fruit = Apple; fruit = Fruit::Orange; +} +// Bond enums are represented as signed 32-bit integers on the wire, but +// implicit conversions allow the comparison to uint32_t values. +void EnumValueLimits() +{ assert(Limits::Int32Min == std::numeric_limits::min()); assert(Limits::Int32Max == std::numeric_limits::max()); assert(Limits::UInt32Min == std::numeric_limits::min()); assert(Limits::UInt32Max == std::numeric_limits::max()); - - return 0; +} + +// The ToEnum and FromEnum functions can be used to convert between enum +// values and their names. ToEnum and FromEnum return a bool indicating +// whether they were successful or not. +void ConversionToFromEnum() +{ + std::string name; + bool result = FromEnum(name, Yellow); + assert(result); + assert(name == "Yellow"); + + result = FromEnum(name, static_cast(100)); + assert(!result); + + Color value; + result = ToEnum(value, "Yellow"); + assert(result); + assert(value == Yellow); + + result = ToEnum(value, "Green"); + assert(!result); +} + +// The ToString and FromString functions can be used to convert between enum +// values and their names. ToString and FromString throw when they encounter +// things they cannot convert. +void ConversionsToFromString() +{ + std::string name = ToString(Yellow); + assert(name == "Yellow"); + + try + { + name = ToString(static_cast(100)); + assert(false); // expected exception + } + catch (const bond::CoreException&) + { + // ToString throws on unknown values. FromEnum is a non-throwing + // alternative. + } + + Color value; + FromString("Yellow", value); + assert(value == Yellow); + + try + { + FromString("Green", value); + assert(false); // expected exception + } + catch (const bond::CoreException&) + { + // FromString throws on unknown names. ToEnum is a non-throwing + // alternative. + } }