Listings Wilkening/Type Traits

Listing 1: Die Ausgabe der Funktion print
#include <iostream>
using namespace std;

template<class T> void print(T t)
{
    cout << "Ko:" << t << ' ';
}

int main()
{
    print(42);
    print(3.14);
    print("Anhalter");
}

// Ausgabe
// Ko:42 Ko:3.14 Ko:Anhalter

-----

Listing 2: Compiler behandelt das Zeichenkettenliteral Anhalter als Zeiger
#include <iostream>
using namespace std;

template<class T> void print(T t)
{
    cout << "Ko:" << t << ' ';
}

template<class T> void print (T* p)
{
    cout << "T*:" << *p << ' ';
}

int main()
{
    print(42);
    print(3.14);
    print("Anhalter");

    int x = 21;
    double d = 2.7;
    print(&x);
    print(&d);
}

// Ausgabe
// Ko:42 Ko:3.14 T*:A T*:21 T*:2.7

-----

Listing 3: Code erhält durch Funktionsüberladung eine normale Funktion für const char*
#include <iostream>
using namespace std;

template<class T> void print(T t)
{
    cout << "Ko:" << t << ' ';
}

template<class T> void print (T* p)
{
    cout << "T*:" << *p << ' ';
}

void print(const char* p)
{
    cout << "C*:" << p << ' ';
}

int main()
{
    print(42);
    print(3.14);
    print("Anhalter");

    int x = 21;
    double d = 2.7;
    print(&x);
    print(&d);
}

// Ausgabe
// Ko:42 Ko:3.14 C*:Anhalter T*:21 T*:2.7

-----

Listing 4: Definition der Typeigenschaft is_basic_v<T>
#include <iostream>
#include <string>
#include <type_traits>
using namespace std;

template<class T> inline constexpr bool is_basic_v = is_fundamental_v<T> || is_enum_v<T>;

template<class T> enable_if_t<is_basic_v<T>> print(T t) 
{
    cout << "Ko:" << t << ' ';
}

template<class T> enable_if_t<!is_basic_v<T>> print(const T& t)
{
    cout << "T&:" << t << ' ';
}

template<class T> void print(T* p)
{
    cout << "T*:" << *p << ' ';
}

void print(const char* p)
{
    cout << "C*:" << p << ' ';
}

enum class E { E1 };
inline ostream& operator<<(ostream& out, E)
{
    return out << 'E';
}

class A {};
inline ostream& operator<<(ostream& out, const A&)
{
    return out << 'A';
}

int main()
{
    print(42);
    print(3.14);
    print("Anhalter");

    int x = 21;
    double d = 2.7;
    print(&x);
    print(&d);

    print(E::E1);
    print("String"s);
    print(A());
}

// Ausgabe
// Ko:42 Ko:3.14 C*:Anhalter T*:21 T*:2.7 Ko:E T&:String T&:A

-----

Listing 5: Benutzerdefiniertes Type Trait name_contains_n
#include <iostream>
using namespace std;

template<class T> struct name_contains_n;

template<> struct name_contains_n<int>
{
    static constexpr bool value = true;
};

template<> struct name_contains_n<long>
{
    static constexpr bool value = true;
};

template<> struct name_contains_n<short>
{
    static constexpr bool value = false;
};

template<> struct name_contains_n<double>
{
    static constexpr bool value = false;
};

int main()
{
    cout << boolalpha;
    cout << "int:    " << name_contains_n<int>::value << '\n';
    cout << "long:   " << name_contains_n<long>::value << '\n';
    cout << "short:  " << name_contains_n<short>::value << '\n';
    cout << "double: " << name_contains_n<double>::value << '\n';
}

// Ausgabe:
// int:        true
// long:      true
// short:    false
// double: false

-----

Listing 6: Benutzerdefiniertes Type Trait name_contains_n mit Default
#include <iostream>
#include <string>
using namespace std;

template<class T> struct name_contains_n
{
    static constexpr bool value = false;
};

template<> struct name_contains_n<int>
{
    static constexpr bool value = true;
};

template<> struct name_contains_n<long>
{
    static constexpr bool value = true;
};

int main()
{
    cout << boolalpha;
    cout << "int:    " << name_contains_n<int>::value << '\n';
    cout << "long:   " << name_contains_n<long>::value << '\n';
    cout << "short:  " << name_contains_n<short>::value << '\n';
    cout << "double: " << name_contains_n<double>::value << '\n';
    cout << "string: " << name_contains_n<string>::value << " - falsch, da fehlende Spezialisierung\n";
}

// Ausgabe:
// int:         true
// long:      true
// short:    false
// double: false
// string:   false - falsch, da fehlende Spezialisierung

-----

Listing 7: Einfacher zu nutzender Type-Trait-Wert in C++14-Art
#include <iostream>
using namespace std;

template<class T> struct name_contains_n;

template<> struct name_contains_n<int>
{
    static constexpr bool value = true;
};

template<> struct name_contains_n<short>
{
    static constexpr bool value = false;
};

template<class T> inline constexpr bool name_contains_n_v = name_contains_n<T>::value;

int main()
{
    cout << boolalpha;
    cout << "int:   " << name_contains_n_v<int> << '\n';
    cout << "short: " << name_contains_n_v<short> << '\n';
}

// Ausgabe:
// int:     true
// short: false

-----

Listing 8: Benutzerdefinierte Erweiterung eines Type Trait
#include <iostream>
using namespace std;

template<class T> struct name_contains_n;

template<> struct name_contains_n<int>
{
    static constexpr bool value = true;
};

template<> struct name_contains_n<short>
{
    static constexpr bool value = false;
};

enum class Enum {};

template<> struct name_contains_n<Enum>
{
    static constexpr bool value = true;
};

enum class Class {};

template<> struct name_contains_n<Class>
{
    static constexpr bool value = false;
};

template<class T> inline constexpr bool name_contains_n_v = name_contains_n<T>::value;

int main()
{
    cout << boolalpha;
    cout << "int:   " << name_contains_n_v<int> << '\n';
    cout << "short: " << name_contains_n_v<short> << '\n';
    cout << "Enum:  " << name_contains_n_v<Enum> << '\n';
    cout << "Class: " << name_contains_n_v<Class> << '\n';
}

// Ausgabe:
// int:       true
// short:   false
// Enum:  true
// Class:   false

-----

Listing 9: Einfacher zu nutzender Type-Trait-Typ in C++14-Art
#include <iostream>
#include <typeinfo>
using namespace std;

template<class T> struct helper_type;

template<> struct helper_type<int>
{
    using type = long;
};

template<> struct helper_type<float>
{
    using type = double;
};

template<class T> using helper_type_t = typename helper_type<T>::type;

int main()
{
    cout << "int:   " << typeid(helper_type_t<int>).name() << '\n';
    cout << "float: " << typeid(helper_type_t<float>).name() << '\n';
}

// Ausgabe
// int:     long
// float: double

-----

Listing 10: Die Funktion FetchField liest einen int-Wert aus
void FetchField(db_cursor& cr, int col, int& val)
{
    if (cr.column_type[col] != DB_INTEGER)
      throw std::runtime_error("DB Typ-Fehler");

    if (!db_access_column(&cr, col))
      throw std::runtime_error("DB Access-Fehler");

    db_integer temp;
    memcpy(&temp, cr.cloumn_data[col], sizeof(temp));

    val = static_cast<int>(temp);
}

-----

Listing 11: Die Funktion FetchField liest einen double-Wert aus
void FetchField(db_cursor& cr, int col, double& val)
{
    if (cr.column_type[col] != DB_FLOATING)
      throw std::runtime_error("DB Typ-Fehler");

    if (!db_access_column(&cr, col))
      throw std::runtime_error("DB Access-Fehler");

    db_floating temp;
    memcpy(&temp, cr.cloumn_data[col], sizeof(temp));

    val = temp.integral_part + temp.tractionary_part/100;
}

-----

Listing 12: Allgemeine Fetch-Funktion mit Traits-Klassen
template<typename T> struct DbTraits;

template<> struct DbTraits<int>
{
    const int TypeId = DB_INTEGER;
    using NativeType = db_integer;
    static void convert(const NativeType& from, int& to)
    {
      to = static_cast<int>(from);
    }
};

template<> struct DbTraits<double>
{
    const int TypeId = DB_FLOATING;
    using NativeType = db_floating;
    static void convert(const NativeType& from, double& to)
    {
      to = from.integral_part + from.tractionary_part/100;
    }
};

template<typename T> void FetchField(db_cursor& cr, int col, T& val)
{
    using Traits = DbTraits<T>;
    if (cr.column_type[col] != Traits::TypeId )
      throw std::runtime_error("DB Typ-Fehler");

    if (!db_access_column(&cr, col))
      throw std::runtime_error("DB Access-Fehler");

    Traits::NativeType temp;
    memcpy(&temp, cr.cloumn_data[col], sizeof(temp));

    Traits::convert(temp, val);
}

-----

Listing 13: Typische Schnittstelle einer Property-Datei-Klasse
class PropertyFile
{
public:
    std::string getValue(const std::string& key) const;
    ...
};

int main()
{
    PropertyFile pf;
    std::string value = pf.getValue("DialogWidth");
    // In "int" konvertieren und beachten, ob "value" ueberhaupt eine Zahl ist
}

-----

Listing 14: Elegantere Schnittstelle einer Property-Datei-Klasse
template<typename T> struct TypeInfo;

template<> struct TypeInfo<int>
{
    static bool isConvertable(const std::string& value)
    {
      size_t size;
      std::stoi(value, &size);
      return size==value.length();
    }

    static int cast(const std::string& value)
    {
      assert(isConvertable(value));
      return std::stoi(value);
    }

    static std::string getName()
    {
      return "Integer";
    }
};

class PropertyFile
{
public:
    template<typename T> T as(const std::string& key) const
    {
      std::string value = getValue(key);
      if (!TypeInfo<T>::isConvertable(value))
      {
        throw std::runtime_error(value + " ist kein " + TypeInfo<T>::getName());
      }
      return TypeInfo<T>::cast(value);
    }
    ...
};

int main()
{
    PropertyFile pf;
    int value = pf.as<int>("DialogWidth");
}

-----

Listing 15: Benutzderdefinierte Type-Trait-Klasse für einen neuen, in einer Property-Datei zu speichernden Typ
enum class Style { Boring, Fancy };

template<> struct TypeInfo<Style>
{
    static bool isConvertable(const std::string& value)
    {
      return value=="Boring" || value=="Fancy";
    }

    static Style cast(const std::string& value)
    {
      assert(isConvertable(value));
      return value=="Boring" ? Style::Boring : Style::Fancy;
    }

    static std::string getName()
    {
      return "Style";
    }
};

int main()
{
    PropertyFile pf;
    Style value = pf.as<Style>("DefaultStyle");
}