String enum - String enum

Ich arbeite im Gaming-Bereich. In dieser Hinsicht müssen ständig mit allen Arten von Konfigurationen umgehen.
Jedes Mal, wenn die Konfigurationen eine bestimmte Aufzählung enthalten sollten, entsteht ein Dilemma. Einerseits möchte ich lesbare Konfigurationen, andererseits schnelles Parsen und schnelle Handhabung dieses Typs.
Was willst du:

"type":
[
	{
		"id": 1,
		"type": "one",
	},
	{
		"id": 2,
		"type": "two",
	},
	{
		"id": 6,
		"type": "three",
	}
]


Gleichzeitig möchte ich Strukturen verwenden wie:

enum Type
{
	one,
	two,
	three
};



Weiter im Hauptteil des Artikels werden verschiedene Lösungen angegeben.



Die Bibliotheksanforderungen waren wie folgt:

  • Plattformübergreifend;
  • Minimale Abhängigkeiten;
  • Lesegeschwindigkeit;
  • Einfache Syntax


Die erste Lösung war vor c ++ 11:

class Type
{
public:
    enum type { one, two, three };
    static const std::string &to_string( type enumVal )
    {
        static const std::map enumStringsMap = _make_enum_strings_map();
        auto it = enumStringsMap.find(enumVal);
        static std::string emptyString;
        if(it==enumStringsMap.end())
            return emptyString;
        return it->second;
    }
    static type from_string(const std::string &value)
    {
        static const std::map stringsEnumMap = _make_strings_enum_map();
        std::map::const_iterator it = stringsEnumMap.find(value);
        if(it==stringsEnumMap.end())
            return (type)0;
        return it->second;
    }
    static const std::vector& values()
    {
        static const std::vector valueVector = _make_values();
        return valueVector;
    }
private:
    static const std::vector _make_values()
    {
        std::vector valueVector;
        valueVector.reserve(3);
        valueVector.push_back(one);
        valueVector.push_back(two);
        valueVector.push_back(three);
        return valueVector;\
    }
    static std::map _make_enum_strings_map()
    {
        std::map enumStringsMap;
        enumStringsMap.insert(std::make_pair(one, "one"));
        enumStringsMap.insert(std::make_pair(two, "two"));
        enumStringsMap.insert(std::make_pair(three, "three"));
        return enumStringsMap;
    }
    static std::map _make_strings_enum_map()
    {
        std::map stringsEnumMap;
        stringsEnumMap.insert(std::make_pair("one", one));
        stringsEnumMap.insert(std::make_pair("two", two));
        stringsEnumMap.insert(std::make_pair("three", three));
        return stringsEnumMap;
    }
};

Неплохо, но писать это для каждого перечисления долговато.

Пример использования:

    Type::type type;
    type =  Type::from_string("one");
    std::string stringType = Type::to_string(type);


В принципе, работает, если to_string, from_string и values не вызываются, то никаких накладных расходов не будет.

Решено было остановится на синтаксисе STRING_ENUM( Type, one, two, three);

Как сделать подобный код на c++ я не представляю, но там, где бессилен c++, помогут Они — макросы (да, неизбежное зло, но писать 60 строк кода для каждого класса очень накладно).

Нашел кусочек решения тут. После небольших доработок получается следующий код, думаю, что 32 — достаточное количество аргументов для большинства задач.

В итоге, после некоторых экспериментов остановился на таком коде:

string_enum.h
#define VA_SIZE(...) INVOKE( VA_GET_SIZE VA_OB INVOKE(VA_SPEC##__VA_ARGS__()), 0, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 VA_CB )
#define VA_OB (
#define VA_CB )
#define VA_SPEC() 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34
#define VA_GET_SIZE(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_,n,...) n
#define INVOKE( ... ) INVOKE_A( __VA_ARGS__ )
#define INVOKE_A( ... ) __VA_ARGS__
#define VA_FOR(macro,data,...) INVOKE( CAT(VA_FOR, VA_SIZE(__VA_ARGS__)) ( macro, data, VA_APPLY(VA_FIRST (__VA_ARGS__)),  (VA_APPLY(VA_WO_FIRST (__VA_ARGS__))) ) )
#define VA_APPLY(x) x
#define VA_FIRST(a, ...) a
#define VA_WO_FIRST(a, ...) __VA_ARGS__
#define VA_FOR0(m,d,e,x)  
#define VA_FOR1(m,d,e,x)  m( d, e ) 
#define VA_FOR2(m,d,e,x)  m( d, e )  VA_FOR1( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR3(m,d,e,x)  m( d, e )  VA_FOR2( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR4(m,d,e,x)  m( d, e )  VA_FOR3( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR5(m,d,e,x)  m( d, e )  VA_FOR4( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR6(m,d,e,x)  m( d, e )  VA_FOR5( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR7(m,d,e,x)  m( d, e )  VA_FOR6( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR8(m,d,e,x)  m( d, e )  VA_FOR7( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR9(m,d,e,x)  m( d, e )  VA_FOR8( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR10(m,d,e,x) m( d, e )  VA_FOR9( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR11(m,d,e,x) m( d, e )  VA_FOR10( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR12(m,d,e,x) m( d, e )  VA_FOR11( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR13(m,d,e,x) m( d, e )  VA_FOR12( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR14(m,d,e,x) m( d, e )  VA_FOR13( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR15(m,d,e,x) m( d, e )  VA_FOR14( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR16(m,d,e,x) m( d, e )  VA_FOR15( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR17(m,d,e,x) m( d, e )  VA_FOR16( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR18(m,d,e,x) m( d, e )  VA_FOR17( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR19(m,d,e,x) m( d, e )  VA_FOR18( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR20(m,d,e,x) m( d, e )  VA_FOR19( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR21(m,d,e,x) m( d, e )  VA_FOR20( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR22(m,d,e,x) m( d, e )  VA_FOR21( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR23(m,d,e,x) m( d, e )  VA_FOR22( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR24(m,d,e,x) m( d, e )  VA_FOR23( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR25(m,d,e,x) m( d, e )  VA_FOR24( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR26(m,d,e,x) m( d, e )  VA_FOR25( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR27(m,d,e,x) m( d, e )  VA_FOR26( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR28(m,d,e,x) m( d, e )  VA_FOR27( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR29(m,d,e,x) m( d, e )  VA_FOR28( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR30(m,d,e,x) m( d, e )  VA_FOR29( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR31(m,d,e,x) m( d, e )  VA_FOR30( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR32(m,d,e,x) m( d, e )  VA_FOR31( m, d, VA_APPLY(VA_FIRST x), (VA_APPLY(VA_WO_FIRST x)))
#define CAT(x,y) CAT_A(x, y)
#define CAT_A(x,y) x##y
#define ENUM_IDENTITY(X,A) A,
#define ENUM_STRING_TO_ENUM(X,A) X.insert(std::make_pair(#A,A));
#define ENUM_ENUM_TO_STRING(X,A) X.insert(std::make_pair(A,#A));
#define ENUM_TO_VECTOR(X,A) X.push_back(A);
#define STRING_ENUM(name, ...) \
class name \
{ \
public:\
    enum Type { VA_FOR(ENUM_IDENTITY, fake, __VA_ARGS__) }; \
    static const std::string &to_string( Type enumVal ) \
    {\
        static const std::map enumStringsMap = _make_enum_strings_map();\
        auto it = enumStringsMap.find(enumVal);\
        static std::string emptyString;\
        if(it==enumStringsMap.end())\
            return emptyString;\
        return it->second;\
    }\
    static Type from_string(const std::string &value)\
    {\
        static const std::map stringsEnumMap = _make_strings_enum_map(); \
        std::map::const_iterator it = stringsEnumMap.find(value);\
        if(it==stringsEnumMap.end())\
            return (Type)0;\
        return it->second;\
    }\
    static const std::vector& values()\
    {\
        static const std::vector valueVector = _make_values();\
        return valueVector;\
    }\
private:\
    static const std::vector _make_values()\
    {\
        std::vector valueVector;\
        valueVector.reserve(VA_SIZE(__VA_ARGS__));\
        VA_FOR(ENUM_TO_VECTOR, valueVector, __VA_ARGS__)\
        return valueVector;\
    }\
    static std::map _make_enum_strings_map()\
    {\
        std::map enumStringsMap;\
        VA_FOR(ENUM_ENUM_TO_STRING, enumStringsMap, __VA_ARGS__)\
        return enumStringsMap;\
    }\
    static std::map _make_strings_enum_map()\
    {\
        std::map stringsEnumMap;\
        VA_FOR(ENUM_STRING_TO_ENUM, stringsEnumMap, __VA_ARGS__)\
        return stringsEnumMap;\
    }\
};


Пример использования:

    STRING_ENUM( MyStringEnum, one, two, three);
    MyStringEnum::Type type;
    type =  MyStringEnum::from_string("one");
    std::string stringType = MyStringEnum::to_string(type);


Плюсы решения компилируется почти во всех компиляторах, лично пробовал на gcc 4.3-4.9 clang 2.8-3.4 MSVC 2012-2013.

Прошло некоторое время и c++11 стал поддерживаться почти всеми компиляторами. Захотелось сделать типобезопасный enum, также решено было вынести вспомогательные ф-ии в отдельный класс, так как enum class глобальную область видимости не засоряет:

enum class MyEnum { ONE, TWO, THREE };
class MyEnumHelper{
public: 
    typedef MyEnum Type;
    static const std::string &to_string( Type enumVal ) 
    {
        static const std::map enumStringsMap = { { Type::ONE, tolower("ONE") }, { Type::TWO, tolower("TWO") }, { Type::THREE, tolower("THREE") } };
        auto it = enumStringsMap.find(enumVal);
        static std::string emptyString;
        if(it==enumStringsMap.end())
            return emptyString;
        return it->second;
    }
    static Type from_string(const std::string &value)
    {
        static const std::map enumStringsMap = { { tolower("ONE"), Type::ONE }, { tolower("TWO"), Type::TWO }, { tolower("THREE"), Type::THREE } };
        auto it = enumStringsMap.find(value);
        if(it==enumStringsMap.end())
            return (Type)0;
        return it->second;
    }
    static const std::vector& values()
    {
        static const std::vector valueVector = { Type::ONE, Type::TWO, Type::THREE };
        return valueVector;
    }
private: 
    inline static char easytolower(char in){ 
        if(in<='Z' && in>='A') 
            return in-('Z'-'z'); 
        return in; 
    }
    static std::string tolower(std::string &&tolower) 
    { 
        std::string temp = tolower; 
        for (std::string::size_type i=0; i


Пример использования:

    Type::Type type;
    type =  Type::from_string("one");
    std::string stringType = Type::to_string(type);


Также в процессе адаптации было принято решение разрешить назначать конкретное значение определенному значению. Идея была подсмотрена тут.

И, наконец, после многих часов отладки макросов препроцессора (помогла эта тема), вот он, финальльный вариант:

string_enum.h
#ifndef _StringEnums_h
#define _StringEnums_h
#include 
#include 
#include 
#define VA_SIZE(...) VA_SIZE_((VA_SIZE_PREFIX_ ## __VA_ARGS__ ## _VA_SIZE_POSTFIX,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0))
#define VA_SIZE_INVOKE(...) INVOKE(VA_SIZE(__VA_ARGS__))
#define VA_SIZE_(__args) VA_GET_SIZE __args
#define VA_SIZE_PREFIX__VA_SIZE_POSTFIX ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0
#define VA_GET_SIZE(__p0,__p1,__p2,__p3,__p4,__p5,__p6,__p7,__p8,__p9,__p10,__p11,__p12,__p13,__p14,__p15,__p16,__p17,__p18,__p19,__p20,__p21,__p22,__p23,__p24,__p25,__p26,__p27,__p28,__p29,__p30,__p31,__n,...) __n
#define INVOKE( ... ) INVOKE_A( __VA_ARGS__ )
#define INVOKE_A( ... ) __VA_ARGS__
#define VA_FOR(macro,...) INVOKE( CAT(VA_FOR, VA_SIZE_INVOKE(__VA_ARGS__)) ( macro, (__VA_ARGS__) ) )
#define VA_APPLY(x) x
#define VA_FIRST(a, ...) a
#define VA_WO_FIRST(a, ...) __VA_ARGS__
#define VA_FOR0(m,x)
#define VA_FOR1(m,x)  m( VA_APPLY(VA_FIRST x) )
#define VA_FOR2(m,x)  m( VA_APPLY(VA_FIRST x) )  VA_FOR1( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR3(m,x)  m( VA_APPLY(VA_FIRST x) )  VA_FOR2( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR4(m,x)  m( VA_APPLY(VA_FIRST x) )  VA_FOR3( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR5(m,x)  m( VA_APPLY(VA_FIRST x) )  VA_FOR4( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR6(m,x)  m( VA_APPLY(VA_FIRST x) )  VA_FOR5( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR7(m,x)  m( VA_APPLY(VA_FIRST x) )  VA_FOR6( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR8(m,x)  m( VA_APPLY(VA_FIRST x) )  VA_FOR7( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR9(m,x)  m( VA_APPLY(VA_FIRST x) )  VA_FOR8( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR10(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR9( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR11(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR10( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR12(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR11( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR13(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR12( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR14(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR13( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR15(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR14( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR16(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR15( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR17(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR16( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR18(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR17( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR19(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR18( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR20(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR19( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR21(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR20( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR22(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR21( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR23(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR22( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR24(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR23( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR25(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR24( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR26(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR25( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR27(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR26( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR28(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR27( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR29(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR28( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR30(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR29( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR31(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR30( m, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR32(m,x) m( VA_APPLY(VA_FIRST x) )  VA_FOR31( m, (VA_APPLY(VA_WO_FIRST x)))
#define CAT(x,y) CAT_A(x, y)
#define CAT_A(x,y) x##y
#define M_STR(A) M_STR_(A)
#define M_STR_(A) #A
#define M_LOWER_STR(A) M_LOWER_STR_(A)
#define M_LOWER_STR_(A) tolower(#A)
#define M_IF(P, T, E) CAT(M_IF_, P)(T, E)
#define M_IF_1(T, E) E
#define M_IF_2(T, E) T
#define M_FIRST(A, ...) A
#define M_SECOND(A, B, ...) B
#define M_ID(...) __VA_ARGS__
#define ENUM_ENAME(A) M_IF(VA_SIZE(M_ID A), M_FIRST A = M_SECOND A, A),
#define ENUM_ELEM(A) M_IF(VA_SIZE(M_ID A), M_FIRST A, A)
#define ENUM_ELEM_TYPE(A) Type::ENUM_ELEM(A)
#define ENUM_ELEM_NAME(A) M_LOWER_STR(ENUM_ELEM(A))
#define ENUM_STRING_TO_TYPE(A) {ENUM_ELEM_NAME(A), ENUM_ELEM_TYPE(A)},
#define ENUM_TYPE_TO_STRING(A) {ENUM_ELEM_TYPE(A), ENUM_ELEM_NAME(A)},
#define ENUM_TYPE(A) ENUM_ELEM_TYPE(A),
#define STRING_ENUM(name, ...) \
enum class name {  VA_FOR(ENUM_ENAME, __VA_ARGS__) };  \
class name##Helper { \
public: \
    typedef name Type; \
    static const std::string &to_string( Type enumVal ) \
    {\
        static const std::map enumStringsMap = { VA_FOR(ENUM_TYPE_TO_STRING, __VA_ARGS__) }; \
        auto it = enumStringsMap.find(enumVal);\
        static std::string emptyString;\
        if(it==enumStringsMap.end())\
            return emptyString;\
        return it->second;\
    }\
    static Type from_string(const std::string &value)\
    {\
        static const std::map enumStringsMap = { VA_FOR(ENUM_STRING_TO_TYPE, __VA_ARGS__) }; \
        auto it = enumStringsMap.find(value);\
        if(it==enumStringsMap.end())\
            return (Type)0;\
        return it->second;\
    }\
    static const std::vector& values()\
    {\
        static const std::vector valueVector = { VA_FOR(ENUM_TYPE, __VA_ARGS__) }; \
        return valueVector;\
    }\
private: \
    inline static char easytolower(char in) \
    { \
        if(in<='Z' && in>='A') \
            return in-('Z'-'z'); \
        return in; \
    }\
    static std::string tolower(std::string &&tolower) \
    { \
        std::string temp = tolower; \
        for (std::string::size_type i=0; i



Вариант использования:

    STRING_ENUM( MyStringEnum, ONE, (TWO,4), THREE);
    MyStringEnum type;
    type =  MyStringEnumHelper::from_string("one");
    std::string stringType = MyStringEnumHelper::to_string(type);


Проверял на gcc 4.6-4.9 clang 3.2-3.3 MSVC 2013.

UPD пример относительно скорости http://ideone.com/1HNGIe

Конструктивная критика и рациональные предложения приветствуются.

Jetzt auch beliebt: