Merged PR 4877: Traits class added
Traits class added
This commit is contained in:
Родитель
7827612fa5
Коммит
455bf4f76c
|
@ -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);
|
||||
}
|
Загрузка…
Ссылка в новой задаче