Merged PR 4877: Traits class added

Traits class added
This commit is contained in:
Michael Sharp 2019-07-31 21:09:38 +00:00
Родитель 7827612fa5
Коммит 455bf4f76c
3 изменённых файлов: 254 добавлений и 0 удалений

214
src/FeaturizerPrep/Traits.h Normal file
Просмотреть файл

@ -0,0 +1,214 @@
// ----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License
// ----------------------------------------------------------------------
#pragma once
#include <vector>
#include <boost/optional.hpp>
#include <map>
#include <array>
namespace Microsoft {
namespace Featurizer {
namespace Traits {
/////////////////////////////////////////////////////////////////////////
/// \namespace Traits
/// \brief We have a range of of types we are dealing with. Many types
/// have different ways to represent what a `NULL` value is
/// (float has NAN for example) as well as different ways to
/// convert the value to a string representation. By using
/// templates combined with partial template specialization
/// we can handle scenarios like these that vary based on the data type.
///
/// Example: This allows us to do things like `Traits<std::int8_t>::IsNull()`
/// and `Traits<float>::IsNull()` and let the trait itself deal with the
/// actual implementation and allows us as developers to not worry about that.
///
/// This benefit is magnified because we are also using templates for our
/// transformers. When we declare that a transformer has type T = std::int8_t,
/// we can then also use `Traits<T>::IsNull()` and the compiler will know that
/// `T` is a `std::int8_t` and call the appropate template specialization.
///
template <typename T>
struct Traits {};
/////////////////////////////////////////////////////////////////////////
/// \namespace Traits
/// \brief When using partial template specilization, if the compiler
/// cannot find a more specfic implementation of the template
/// it will fall back to the base template and use whatever is
/// defined there. If you have methods defined in that base template,
/// it makes it very difficult to debug what is going on. By
/// putting no implementation in the `Traits<>` template and
/// having the real base struct be `TraitsImpl<>`, if you try and
/// specify a trait that doesn't have a specilization, the compiler
/// can detect that and throw an error during compilation.
///
/// Example: There is no template `Traits<char>`. If you try and use it
/// the compiler will fall back to the `Traits<>` struct which has no methods
/// defined. Trying to then use `Traits<char>` will cause a compile time error
/// letting you know something isn't correct.
///
template <typename T>
struct TraitsImpl {
using nullable_type = boost::optional<T>;
static bool IsNull(nullable_type const& value) {
return !value.is_initialized();
}
};
template <>
struct Traits<float> : public TraitsImpl<float> {
using nullable_type = float;
static bool IsNull(nullable_type const& value) {
return isnan(value);
}
// static std::string ToString(nullable_type const& value) {
// return std::to_string(value);
// }
};
template <>
struct Traits<double> : public TraitsImpl<double> {
using nullable_type = double;
static bool IsNull(nullable_type const& value) {
return isnan(value);
}
// static std::string ToString(nullable_type const& value) {
// return std::to_string(value);
// }
};
template <>
struct Traits<std::int8_t> : public TraitsImpl<std::int8_t> {
// static std::string ToString(std::int8_t const& value) {
// return std::to_string(value);
// }
};
template <>
struct Traits<std::int16_t> : public TraitsImpl<std::int16_t> {
// static std::string ToString(std::int16_t const& value) {
// return std::to_string(value);
// }
};
template <>
struct Traits<std::int32_t> : public TraitsImpl<std::int32_t> {
// static std::string ToString(std::int32_t const& value) {
// return std::to_string(value);
// }
};
template <>
struct Traits<std::int64_t> : public TraitsImpl<std::int64_t> {
// static std::string ToString(std::int64_t const& value) {
// return std::to_string(value);
// }
};
template <>
struct Traits<std::uint8_t> : public TraitsImpl<std::uint8_t> {
// static std::string ToString(std::uint8_t const& value) {
// return std::to_string(value);
// }
};
template <>
struct Traits<std::uint16_t> : public TraitsImpl<std::uint16_t> {
using nullable_type = boost::optional<std::uint16_t>;
// static std::string ToString(std::uint16_t const& value) {
// return std::to_string(value);
// }
};
template <>
struct Traits<std::uint32_t> : public TraitsImpl<std::uint32_t> {
// static std::string ToString(std::uint32_t const& value) {
// return std::to_string(value);
// }
};
template <>
struct Traits<std::uint64_t> : public TraitsImpl<std::uint64_t> {
// static std::string ToString(std::uint64_t const& value) {
// return std::to_string(value);
// }
};
template <>
struct Traits<std::string> : public TraitsImpl<std::string> {
// static std::string ToString(std::string const& value) {
// value;
// }
};
template <typename T, size_t size>
struct Traits <std::array<T, size>> : public TraitsImpl<std::array<T, size>> {
// static std::string ToString(std::array<T, size> const& value) {
// // Decide what to return here
// throw std::logic_error("Function not yet implemented");
// }
};
template <>
struct Traits<bool> : public TraitsImpl<bool> {
// static std::string ToString(bool const& value) {
// // Decide what to return here
// throw std::logic_error("Function not yet implemented");
// }
};
template <typename KeyT, typename T, typename CompareT, typename AllocatorT>
struct Traits<std::map<KeyT, T, CompareT, AllocatorT>> : public TraitsImpl<std::map<KeyT, T, CompareT, AllocatorT>> {
// static std::string ToString(std::map<KeyT, T, CompareT, AllocatorT> const& value) {
// // Decide what to return here
// throw std::logic_error("Function not yet implemented");
// }
};
template <typename T, typename AllocatorT>
struct Traits<std::vector<T, AllocatorT>> : public TraitsImpl<std::vector<T, AllocatorT>> {
// static std::string ToString(std::vector<T, AllocatorT> const& value) {
// // Decide what to return here
// throw std::logic_error("Function not yet implemented");
// }
};
template <typename ... Types>
struct Traits <std::function<Types...>> : public TraitsImpl<std::function<Types...>> {
// static std::string ToString(std::function<Types ...> const& value) {
// // Decide what to return here
// throw std::logic_error("Function not yet implemented");
// }
};
template <typename T>
struct Traits <boost::optional<T>> : public TraitsImpl<boost::optional<T>> {
using nullable_type = boost::optional<T>;
// static std::string ToString(nullable_type const& value) {
// if (value) {
// return Traits<T>::ToString(value.get());
// }
// return "NULL";
// }
};
template <typename ... Types>
struct Traits <std::tuple<Types...>> : public TraitsImpl<std::tuple<Types...>> {
// static std::string ToString(std::tuple<Types ...> const& value) {
// // Decide what to return here
// throw std::logic_error("Function not yet implemented");
// }
};
} // namespace Traits
} // namespace Featurizer
} // namespace Microsoft

Просмотреть файл

@ -27,6 +27,7 @@ enable_testing()
foreach(_test_name IN ITEMS
Featurizer_UnitTest
Traits_UnitTests
)
add_executable(${_test_name} ${_test_name}.cpp)

Просмотреть файл

@ -0,0 +1,39 @@
// ----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License
// ----------------------------------------------------------------------
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "../Traits.h"
#include <type_traits>
using namespace Microsoft::Featurizer::Traits;
// Floating point values
static_assert(std::is_same<Traits<float>::nullable_type, float>::value, "Incorrect nullable type for float");
static_assert(std::is_same<Traits<double>::nullable_type, double>::value, "Incorrect nullable type for double");
// Int values
static_assert(std::is_same<Traits<std::int8_t>::nullable_type, boost::optional<std::int8_t>>::value, "Incorrect nullable type for std::int8_t");
static_assert(std::is_same<Traits<std::int16_t>::nullable_type, boost::optional<std::int16_t>>::value, "Incorrect nullable type for std::int16_t");
static_assert(std::is_same<Traits<std::int32_t>::nullable_type, boost::optional<std::int32_t>>::value, "Incorrect nullable type for std::int32_t");
static_assert(std::is_same<Traits<std::int64_t>::nullable_type, boost::optional<std::int64_t>>::value, "Incorrect nullable type for std::int64_t");
static_assert(std::is_same<Traits<std::uint8_t>::nullable_type, boost::optional<std::uint8_t>>::value, "Incorrect nullable type for std::uint8_t");
static_assert(std::is_same<Traits<std::uint16_t>::nullable_type, boost::optional<std::uint16_t>>::value, "Incorrect nullable type for std::uint16_t");
static_assert(std::is_same<Traits<std::uint32_t>::nullable_type, boost::optional<std::uint32_t>>::value, "Incorrect nullable type for std::uint32_t");
static_assert(std::is_same<Traits<std::uint64_t>::nullable_type, boost::optional<std::uint64_t>>::value, "Incorrect nullable type for std::uint64_t");
// Others
static_assert(std::is_same<Traits<std::string>::nullable_type, boost::optional<std::string>>::value, "Incorrect nullable type for std::string");
static_assert(std::is_same<Traits<std::array<char, 4>>::nullable_type, boost::optional<std::array<char, 4>>>::value, "Incorrect nullable type for std::array");
static_assert(std::is_same<Traits<bool>::nullable_type, boost::optional<bool>>::value, "Incorrect nullable type for std::string");
static_assert(std::is_same<Traits<std::map<int,int>>::nullable_type, boost::optional<std::map<int,int>>>::value, "Incorrect nullable type for std::string");
static_assert(std::is_same<Traits<std::vector<int>>::nullable_type, boost::optional<std::vector<int>>>::value, "Incorrect nullable type for std::string");
static_assert(std::is_same<Traits<std::function<int>>::nullable_type, boost::optional<std::function<int>>>::value, "Incorrect nullable type for std::string");
static_assert(std::is_same<Traits<boost::optional<int>>::nullable_type, boost::optional<int>>::value, "Incorrect nullable type for std::string");
static_assert(std::is_same<Traits<std::tuple<int>>::nullable_type, boost::optional<std::tuple<int>>>::value, "Incorrect nullable type for std::string");
// Dummy test so it will compile. Replace this with actual tests.
TEST_CASE("Dummy Test") {
CHECK(true == true);
}