Skip to content

Commit

Permalink
Add enum_cast.
Browse files Browse the repository at this point in the history
enum_cast as a simple and consistent way of casting from enums
to strings and back the other way.
  • Loading branch information
Richard Mills committed Aug 6, 2019
1 parent 6cb6016 commit cc10f4b
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/Makefile.am
Expand Up @@ -110,6 +110,7 @@ BITCOIN_CORE_H = \
core_memusage.h \
cuckoocache.h \
dstencode.h \
enum_cast.h \
fs.h \
httprpc.h \
httpserver.h \
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.test.include
Expand Up @@ -50,6 +50,7 @@ BITCOIN_TESTS =\
test/dbwrapper_tests.cpp \
test/DoS_tests.cpp \
test/dstencode_tests.cpp \
test/enum_cast_tests.cpp \
test/excessiveblock_tests.cpp \
test/getarg_tests.cpp \
test/hash_tests.cpp \
Expand Down
106 changes: 106 additions & 0 deletions src/enum_cast.h
@@ -0,0 +1,106 @@
// Copyright (c) 2019 Bitcoin Association.
// Distributed under the Open BSV software license, see the accompanying file LICENSE.

/*
* A general purpose mechanism for casting between enums and strings.
*
* Given an enumeration, we also provide a function enumTable() that returns
* an enumTableT specifying a mapping between the enumeration and the
* castable string values. With that in place we can perform casting with
* enum_cast<Enum>(string) or enum_cast<string>(Enum).
*
* Eg:
*
* enum class MyTypes { UNKNOWN, Type1, Type2 };
*
* const enumTableT<MyTypes, std::string>& enumTable(MyTypes, std::string)
* {
* static enumTableT<MyTypes, std::string> table
* {
* {MyTypes::UNKNOWN, "Unknown"}, {MyTypes::Type1, "Type 1"}, {MyTypes::Type2, "Type 2"}
* };
* return table;
* }
*
* std::string str { enum_cast<std::string>(MyTypes::Type1) };
* MyTypes mytype { enum_cast<MyTypes>(str) };
*/

#pragma once

#include <string>
#include <unordered_map>

// The type returned by all enum_table() functions.
template <typename From>
class enumTableT
{
public:

// Constructor - requires table.size() > 0
enumTableT(std::initializer_list<std::pair<const From, std::string>> table)
: mLookupTable{table}, mDefaultValue{*table.begin()}
{
// Populate reverse lookup table
for(const auto& item : mLookupTable)
{
mReverseLookupTable[item.second] = item.first;
}
}

// Cast from enum to string
template <typename CastType>
const std::string& castToString(const CastType& from) const
{
auto it { mLookupTable.find(from) };
if(it != mLookupTable.end())
{
return it->second;
}
return mDefaultValue.second;
}

// Cast from string to enum
template <typename CastType>
const From& castToEnum(const CastType& to) const
{
auto it { mReverseLookupTable.find(to) };
if(it != mReverseLookupTable.end())
{
return it->second;
}
return mDefaultValue.first;
}

private:

using LookupTable = std::unordered_map<From, std::string>;
using ReverseLookupTable = std::unordered_map<std::string, From>;

LookupTable mLookupTable {};
ReverseLookupTable mReverseLookupTable {};
typename LookupTable::value_type mDefaultValue {};

};

// Cast to string
template<typename ToType = std::string, typename FromType>
std::string enum_cast(const FromType& value)
{
return enumTable(FromType{}).castToString(value);
}

// Cast from string
template<typename ToType>
ToType enum_cast(const std::string& value)
{
return enumTable(ToType{}).castToEnum(value);
}

// Cast from convertable to string
template<typename ToType>
ToType enum_cast(const char* value)
{
return enumTable(ToType{}).castToEnum(value);
}

1 change: 1 addition & 0 deletions src/test/CMakeLists.txt
Expand Up @@ -73,6 +73,7 @@ add_test_to_suite(bitcoin test_bitcoin
dbwrapper_tests.cpp
DoS_tests.cpp
dstencode_tests.cpp
enum_cast_tests.cpp
excessiveblock_tests.cpp
getarg_tests.cpp
hash_tests.cpp
Expand Down
55 changes: 55 additions & 0 deletions src/test/enum_cast_tests.cpp
@@ -0,0 +1,55 @@
// Copyright (c) 2019 Bitcoin Association.
// Distributed under the Open BSV software license, see the accompanying file LICENSE.

#include "enum_cast.h"
#include <boost/test/unit_test.hpp>

namespace
{
// Table for casting to/from string
enum class MyTypesCorrect { UNKNOWN, Type1, Type2 };
const enumTableT<MyTypesCorrect>& enumTable(MyTypesCorrect)
{
static enumTableT<MyTypesCorrect> table
{
{MyTypesCorrect::UNKNOWN, "Unknown"}, {MyTypesCorrect::Type1, "Type 1"}, {MyTypesCorrect::Type2, "Type 2"}
};
return table;
}

// Output operator
std::ostream& operator<<(std::ostream& str, MyTypesCorrect mytype)
{
str << enum_cast<std::string>(mytype);
return str;
}
}

BOOST_AUTO_TEST_SUITE(TestEnumCast);

// Test normal (correct) operation of enum_cast
BOOST_AUTO_TEST_CASE(TestCorrectEnumCast)
{
// Cast from existing string and back (non-fundamental type)
std::string str { "Type 1" };
MyTypesCorrect myType { enum_cast<MyTypesCorrect>(str) };
BOOST_CHECK_EQUAL(myType, MyTypesCorrect::Type1);
str = enum_cast<std::string>(myType);
BOOST_CHECK_EQUAL(str, "Type 1");

// Cast from convertable to string
myType = enum_cast<MyTypesCorrect>("Type 1");
BOOST_CHECK_EQUAL(myType, MyTypesCorrect::Type1);
}

// Test UNKNOWN casting
BOOST_AUTO_TEST_CASE(TestUnknownEnumCast)
{
// Cast to MyTypes from unknown string
std::string str { "Wibble" };
MyTypesCorrect myType { enum_cast<MyTypesCorrect>(str) };
BOOST_CHECK_EQUAL(myType, MyTypesCorrect::UNKNOWN);
}

BOOST_AUTO_TEST_SUITE_END();

0 comments on commit cc10f4b

Please sign in to comment.