Unverified Commit 9c3c3c5f authored by Paul Fultz II's avatar Paul Fultz II Committed by GitHub
Browse files

Add serialization framework (#577)



* Add initial serialization

* Formatting

* Add unit tests

* Formatting

* Add tests for serialization

* Formatting

* Use or not and

* Add value test

* Formatting

* Add more tests

* Add shape serialization

* Formatting

* Add serializtion for literal and argument

* Formatting

* Serialize empty types

* Formatting

* Tidy fixes

* Formatting

* Fix tidy issues

* Formatting

* Reformat value type macro

* Formatting

* Handle enum types

* Formatting

* Add float_equal

* Fix tidy issue

* Use declval for better sfinae

* Formatting

* Fix maro name

* Add more test cases to improve coverage

* Formatting

* Add more tests

* Formatting

* Fix assertion

* Fix bug with keyless assignment

* Formatting
Co-authored-by: default avatarmvermeulen <5479696+mvermeulen@users.noreply.github.com>
parent 5aec7029
...@@ -24,9 +24,11 @@ add_library(migraphx ...@@ -24,9 +24,11 @@ add_library(migraphx
remap.cpp remap.cpp
shape.cpp shape.cpp
schedule.cpp schedule.cpp
serialize.cpp
pass_manager.cpp pass_manager.cpp
simplify_algebra.cpp simplify_algebra.cpp
simplify_reshapes.cpp simplify_reshapes.cpp
value.cpp
opt/memory_coloring.cpp opt/memory_coloring.cpp
opt/memory_coloring_impl.cpp opt/memory_coloring_impl.cpp
) )
......
...@@ -73,6 +73,9 @@ struct argument : raw_data<argument> ...@@ -73,6 +73,9 @@ struct argument : raw_data<argument>
shape m_shape; shape m_shape;
}; };
void migraphx_to_value(value& v, const argument& a);
void migraphx_from_value(const value& v, argument& a);
} // namespace MIGRAPHX_INLINE_NS } // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx } // namespace migraphx
......
#ifndef MIGRAPHX_GUARD_RTGLIB_CLONEABLE_HPP
#define MIGRAPHX_GUARD_RTGLIB_CLONEABLE_HPP
#include <migraphx/config.hpp>
#include <memory>
namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {
template <typename Base>
struct cloneable
{
friend Base;
virtual std::shared_ptr<Base> clone() = 0;
template <typename Derived>
struct derive : Base
{
friend Derived;
std::shared_ptr<Base> clone()
{
return std::make_shared<Derived>(static_cast<const Derived&>(*this));
}
template <typename... Args>
derive(Args&&... args) : Base(std::forward<Args>(args)...)
{
}
};
struct share : Base, std::enable_shared_from_this<Base>
{
std::shared_ptr<Base> clone() { return this->shared_from_this(); }
template <typename... Args>
share(Args&&... args) : Base(std::forward<Args>(args)...)
{
}
};
cloneable() = default;
cloneable(const cloneable&) = default;
cloneable& operator=(const cloneable&) = default;
virtual ~cloneable() {}
};
} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx
#endif
...@@ -125,6 +125,9 @@ literal transform(literal l1, literal l2, F f) ...@@ -125,6 +125,9 @@ literal transform(literal l1, literal l2, F f)
return result; return result;
} }
void migraphx_to_value(value& v, const literal& l);
void migraphx_from_value(const value& v, literal& l);
} // namespace MIGRAPHX_INLINE_NS } // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx } // namespace migraphx
......
#ifndef MIGRAPHX_GUARD_RTGLIB_SERIALIZE_HPP
#define MIGRAPHX_GUARD_RTGLIB_SERIALIZE_HPP
#include <migraphx/config.hpp>
#include <migraphx/value.hpp>
#include <migraphx/reflect.hpp>
#include <migraphx/requires.hpp>
#include <migraphx/rank.hpp>
#include <type_traits>
namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {
template <class T>
value to_value(const T& x);
template <class T>
void from_value(const value& v, T& x);
template <class T>
T from_value(const value& v)
{
T x;
from_value(v, x);
return x;
}
namespace detail {
template <class T, MIGRAPHX_REQUIRES(std::is_empty<T>{})>
value to_value_impl(rank<0>, const T&)
{
return value::object{};
}
template <class T, class U>
value to_value_impl(rank<1>, const std::pair<T, U>& x)
{
return {x.first, x.second};
}
template <class T>
auto to_value_impl(rank<2>, const T& x) -> decltype(x.begin(), x.end(), value{})
{
value result;
for(auto&& y : x)
{
auto e = to_value(y);
result.insert(to_value(y));
}
return result;
}
template <class T, MIGRAPHX_REQUIRES(is_reflectable<T>{})>
value to_value_impl(rank<3>, const T& x)
{
value result;
reflect_each(x, [&](auto&& y, std::string name) { result.emplace(name, to_value(y)); });
return result;
}
template <class T, MIGRAPHX_REQUIRES(std::is_signed<T>{})>
value to_value_impl(rank<4>, const T& x)
{
return std::int64_t{x};
}
template <class T, MIGRAPHX_REQUIRES(std::is_unsigned<T>{})>
value to_value_impl(rank<5>, const T& x)
{
return std::uint64_t{x};
}
template <class T, MIGRAPHX_REQUIRES(std::is_floating_point<T>{})>
value to_value_impl(rank<6>, const T& x)
{
return double{x};
}
template <class T, MIGRAPHX_REQUIRES(std::is_enum<T>{})>
value to_value_impl(rank<7>, const T& x)
{
return static_cast<std::underlying_type_t<T>>(x);
}
inline value to_value_impl(rank<8>, const std::string& x) { return x; }
template <class T>
auto to_value_impl(rank<9>, const T& x) -> decltype(migraphx_to_value(x))
{
return migraphx_to_value(x);
}
template <class T>
auto to_value_impl(rank<10>, const T& x)
-> decltype(migraphx_to_value(std::declval<value&>(), x), value{})
{
value v;
migraphx_to_value(v, x);
return v;
}
template <class T, MIGRAPHX_REQUIRES(std::is_empty<T>{})>
void from_value_impl(rank<0>, const value& v, T& x)
{
if(not v.is_object())
MIGRAPHX_THROW("Expected an object");
if(not v.get_object().empty())
MIGRAPHX_THROW("Expected an empty object");
x = T{};
}
template <class T>
auto from_value_impl(rank<1>, const value& v, T& x)
-> decltype(x.insert(x.end(), *x.begin()), void())
{
x.clear();
for(auto&& e : v)
x.insert(x.end(), from_value<typename T::value_type>(e));
}
template <class T>
auto from_value_impl(rank<2>, const value& v, T& x) -> decltype(x.insert(*x.begin()), void())
{
x.clear();
for(auto&& e : v)
x.emplace(e.get_key(), from_value<typename T::mapped_type>(e));
}
template <class T, MIGRAPHX_REQUIRES(is_reflectable<T>{})>
void from_value_impl(rank<3>, const value& v, T& x)
{
reflect_each(x, [&](auto&& y, const std::string& name) {
using type = std::decay_t<decltype(y)>;
y = from_value<type>(v.at(name).without_key());
});
}
template <class T, MIGRAPHX_REQUIRES(std::is_arithmetic<T>{})>
void from_value_impl(rank<4>, const value& v, T& x)
{
x = v.to<T>();
}
template <class T, MIGRAPHX_REQUIRES(std::is_enum<T>{})>
void from_value_impl(rank<5>, const value& v, T& x)
{
x = static_cast<T>(v.to<std::underlying_type_t<T>>());
}
inline void from_value_impl(rank<6>, const value& v, std::string& x) { x = v.to<std::string>(); }
template <class T>
auto from_value_impl(rank<7>, const value& v, T& x) -> decltype(migraphx_from_value(v, x), void())
{
migraphx_from_value(v, x);
}
} // namespace detail
template <class T>
value to_value(const T& x)
{
return detail::to_value_impl(rank<10>{}, x);
}
template <class T>
void from_value(const value& v, T& x)
{
detail::from_value_impl(rank<7>{}, v, x);
}
} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx
#endif
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
namespace migraphx { namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS { inline namespace MIGRAPHX_INLINE_NS {
struct value;
struct shape_impl; struct shape_impl;
struct shape struct shape
...@@ -184,6 +185,7 @@ struct shape ...@@ -184,6 +185,7 @@ struct shape
} }
std::string type_string() const; std::string type_string() const;
static type_t parse_type(const std::string& s);
private: private:
std::shared_ptr<const shape_impl> impl; std::shared_ptr<const shape_impl> impl;
...@@ -191,6 +193,9 @@ struct shape ...@@ -191,6 +193,9 @@ struct shape
std::size_t element_space() const; std::size_t element_space() const;
}; };
void migraphx_to_value(value& v, const shape& s);
void migraphx_from_value(const value& v, shape& s);
} // namespace MIGRAPHX_INLINE_NS } // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx } // namespace migraphx
......
#ifndef MIGRAPHX_GUARD_RTGLIB_VALUE_HPP
#define MIGRAPHX_GUARD_RTGLIB_VALUE_HPP
#include <migraphx/config.hpp>
#include <migraphx/errors.hpp>
#include <migraphx/requires.hpp>
#include <migraphx/type_name.hpp>
#include <migraphx/rank.hpp>
#include <algorithm>
#include <memory>
#include <sstream>
#include <type_traits>
#include <tuple>
#include <unordered_map>
#include <vector>
namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {
struct value_base_impl;
template <class To>
struct value_converter
{
template <class T = To>
static auto apply(const std::string& x)
-> decltype((std::declval<std::stringstream&>() >> std::declval<T&>()), To{})
{
To result;
std::stringstream ss;
ss.str(x);
ss >> result;
if(ss.fail())
throw std::runtime_error("Failed to parse: " + x);
return result;
}
template <class From, MIGRAPHX_REQUIRES(std::is_convertible<From, To>{})>
static To apply(const From& x)
{
return To(x);
}
};
template <>
struct value_converter<std::string>
{
static const std::string& apply(const std::string& x) { return x; }
static std::string apply(const std::nullptr_t&) { return "null"; }
template <class From>
static auto apply(const From& x)
-> decltype(std::declval<std::stringstream&>() << x, std::string())
{
std::stringstream ss;
ss << x;
if(ss.fail())
throw std::runtime_error("Failed to parse");
return ss.str();
}
};
template <class T, class U>
struct value_converter<std::pair<T, U>>
{
template <class Key, class From>
static auto apply(const std::pair<Key, From>& x)
-> decltype(std::pair<T, U>(x.first, value_converter<U>::apply(x.second)))
{
return std::pair<T, U>(x.first, value_converter<U>::apply(x.second));
}
};
namespace detail {
template <class To, class From>
auto try_convert_value_impl(rank<1>, const From& x) -> decltype(value_converter<To>::apply(x))
{
return value_converter<To>::apply(x);
}
template <class To, class From>
To try_convert_value_impl(rank<0>, const From& x)
{
MIGRAPHX_THROW("Incompatible values: " + get_type_name(x) + " -> " + get_type_name<To>());
}
} // namespace detail
template <class To, class From>
To try_convert_value(const From& x)
{
return detail::try_convert_value_impl<To>(rank<1>{}, x);
}
struct value
{
// clang-format off
#define MIGRAPHX_VISIT_VALUE_TYPES(m) \
m(int64, std::int64_t) \
m(uint64, std::uint64_t) \
m(float, double) \
m(string, std::string) \
m(bool, bool)
// clang-format on
enum type_t
{
#define MIGRAPHX_VALUE_GENERATE_ENUM_TYPE(vt, cpp_type) vt##_type,
MIGRAPHX_VISIT_VALUE_TYPES(MIGRAPHX_VALUE_GENERATE_ENUM_TYPE) object_type,
array_type,
null_type
#undef MIGRAPHX_VALUE_GENERATE_ENUM_TYPE
};
using iterator = value*;
using const_iterator = const value*;
using value_type = value;
using key_type = std::string;
using mapped_type = value;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using array = std::vector<value>;
using object = std::unordered_map<std::string, value>;
value() = default;
value(const value& rhs);
value& operator=(value rhs);
value(const std::string& pkey, const value& rhs);
value(const std::initializer_list<value>& i);
value(const std::vector<value>& v, bool array_on_empty = true);
value(const std::unordered_map<std::string, value>& m);
value(const std::string& pkey, const std::vector<value>& v, bool array_on_empty = true);
value(const std::string& pkey, const std::unordered_map<std::string, value>& m);
value(const std::string& pkey, std::nullptr_t);
value(const char* i);
#define MIGRAPHX_VALUE_GENERATE_DECL_METHODS(vt, cpp_type) \
value(cpp_type i); \
value(const std::string& pkey, cpp_type i); \
value& operator=(cpp_type rhs); \
bool is_##vt() const; \
const cpp_type& get_##vt() const; \
const cpp_type* if_##vt() const;
MIGRAPHX_VISIT_VALUE_TYPES(MIGRAPHX_VALUE_GENERATE_DECL_METHODS)
template <class T>
using pick = std::conditional_t<
std::is_floating_point<T>{},
double,
std::conditional_t<std::is_signed<T>{},
std::int64_t,
std::conditional_t<std::is_unsigned<T>{}, std::uint64_t, T>>>;
template <class T>
using is_pickable =
std::integral_constant<bool, (std::is_arithmetic<T>{} and not std::is_pointer<T>{})>;
template <class T, MIGRAPHX_REQUIRES(is_pickable<T>{})>
value(T i) : value(pick<T>{i})
{
}
template <class T, MIGRAPHX_REQUIRES(is_pickable<T>{})>
value(const std::string& pkey, T i) : value(pkey, pick<T>{i})
{
}
template <class T, class U, class = decltype(value(T{}, U{}))>
value(const std::pair<T, U>& p) : value(p.first, p.second)
{
}
template <class T, MIGRAPHX_REQUIRES(is_pickable<T>{})>
value& operator=(T rhs)
{
return *this = pick<T>{rhs}; // NOLINT
}
bool is_array() const;
const std::vector<value>& get_array() const;
const std::vector<value>* if_array() const;
bool is_object() const;
const std::vector<value>& get_object() const;
const std::vector<value>* if_object() const;
bool is_null() const;
const std::string& get_key() const;
value* find(const std::string& pkey);
const value* find(const std::string& pkey) const;
bool contains(const std::string& pkey) const;
std::size_t size() const;
bool empty() const;
const value* data() const;
value* data();
value* begin();
const value* begin() const;
value* end();
const value* end() const;
value& front();
const value& front() const;
value& back();
const value& back() const;
value& at(std::size_t i);
const value& at(std::size_t i) const;
value& at(const std::string& pkey);
const value& at(const std::string& pkey) const;
value& operator[](std::size_t i);
const value& operator[](std::size_t i) const;
value& operator[](const std::string& pkey);
std::pair<value*, bool> insert(const value& v);
value* insert(const value* pos, const value& v);
template <class... Ts>
std::pair<value*, bool> emplace(Ts&&... xs)
{
return insert(value(std::forward<Ts>(xs)...));
}
template <class... Ts>
value* emplace(const value* pos, Ts&&... xs)
{
return insert(pos, value(std::forward<Ts>(xs)...));
}
void push_back(const value& v) { insert(end(), v); }
void push_front(const value& v) { insert(begin(), v); }
value with_key(const std::string& pkey) const;
value without_key() const;
template <class Visitor>
void visit(Visitor v) const
{
switch(this->get_type())
{
case null_type:
{
v(std::nullptr_t{});
return;
}
#define MIGRAPHX_VALUE_GENERATE_CASE(vt, cpp_type) \
case vt##_type: \
{ \
if(this->key.empty()) \
v(this->get_##vt()); \
else \
v(std::make_pair(this->get_key(), std::ref(this->get_##vt()))); \
return; \
}
MIGRAPHX_VISIT_VALUE_TYPES(MIGRAPHX_VALUE_GENERATE_CASE)
MIGRAPHX_VALUE_GENERATE_CASE(array, )
MIGRAPHX_VALUE_GENERATE_CASE(object, )
}
MIGRAPHX_THROW("Unknown type");
}
template <class To>
To to() const
{
To result;
this->visit([&](auto y) { result = try_convert_value<To>(y); });
return result;
}
template <class To>
std::vector<To> to_vector() const
{
std::vector<To> result;
const auto& values = is_object() ? get_object() : get_array();
result.reserve(values.size());
std::transform(values.begin(), values.end(), std::back_inserter(result), [&](auto v) {
return v.template to<To>();
});
return result;
}
friend bool operator==(const value& x, const value& y);
friend bool operator!=(const value& x, const value& y);
friend bool operator<(const value& x, const value& y);
friend bool operator<=(const value& x, const value& y);
friend bool operator>(const value& x, const value& y);
friend bool operator>=(const value& x, const value& y);
friend std::ostream& operator<<(std::ostream& os, const value& d);
void debug_print() const;
private:
type_t get_type() const;
std::shared_ptr<value_base_impl> x;
std::string key;
};
} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx
#endif
#include <migraphx/serialize.hpp>
#include <migraphx/argument.hpp>
#include <migraphx/literal.hpp>
namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {
template <class RawData>
void raw_data_to_value(value& v, const RawData& rd)
{
value result;
result["shape"] = migraphx::to_value(rd.get_shape());
rd.visit([&](auto x) { result["data"] = std::vector<value>(x.begin(), x.end()); });
v = result;
}
void migraphx_to_value(value& v, const literal& l) { raw_data_to_value(v, l); }
void migraphx_from_value(const value& v, literal& l)
{
auto s = migraphx::from_value<shape>(v.at("shape"));
s.visit_type([&](auto as) {
using type = typename decltype(as)::type;
l = literal{s, v.at("data").to_vector<type>()};
});
}
void migraphx_to_value(value& v, const argument& a) { raw_data_to_value(v, a); }
void migraphx_from_value(const value& v, argument& a)
{
literal l = migraphx::from_value<literal>(v);
a = l.get_argument();
}
} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx
#include <migraphx/shape.hpp> #include <migraphx/shape.hpp>
#include <migraphx/stringutils.hpp> #include <migraphx/stringutils.hpp>
#include <migraphx/serialize.hpp>
#include <numeric> #include <numeric>
#include <algorithm> #include <algorithm>
#include <functional> #include <functional>
#include <unordered_map>
#include <iostream> #include <iostream>
namespace migraphx { namespace migraphx {
...@@ -231,5 +233,28 @@ std::ostream& operator<<(std::ostream& os, const shape& x) ...@@ -231,5 +233,28 @@ std::ostream& operator<<(std::ostream& os, const shape& x)
return os; return os;
} }
shape::type_t shape::parse_type(const std::string& s)
{
static std::unordered_map<std::string, shape::type_t> m = {
#define MIGRAPHX_SHAPE_GENERATE_TYPE_STRING_MAP(x, t) {#x, x}, {#t, x},
MIGRAPHX_SHAPE_VISIT_TYPES(MIGRAPHX_SHAPE_GENERATE_TYPE_STRING_MAP)};
return m.at(s);
}
void migraphx_to_value(value& v, const shape& s)
{
value result;
result["type"] = migraphx::to_value(s.type_string());
result["lens"] = migraphx::to_value(s.lens());
result["strides"] = migraphx::to_value(s.strides());
v = result;
}
void migraphx_from_value(const value& v, shape& s)
{
s = shape{shape::parse_type(v.at("type").get_string()),
v.at("lens").to_vector<std::size_t>(),
v.at("strides").to_vector<std::size_t>()};
}
} // namespace MIGRAPHX_INLINE_NS } // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx } // namespace migraphx
#include <cassert>
#include <iostream>
#include <migraphx/cloneable.hpp>
#include <migraphx/errors.hpp>
#include <migraphx/stringutils.hpp>
#include <migraphx/value.hpp>
#include <unordered_map>
#include <utility>
namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {
struct value_base_impl : cloneable<value_base_impl>
{
virtual value::type_t get_type() { return value::null_type; }
#define MIGRAPHX_VALUE_GENERATE_BASE_FUNCTIONS(vt, cpp_type) \
virtual const cpp_type* if_##vt() const { return nullptr; }
MIGRAPHX_VISIT_VALUE_TYPES(MIGRAPHX_VALUE_GENERATE_BASE_FUNCTIONS)
virtual std::vector<value>* if_array() { return nullptr; }
virtual std::unordered_map<std::string, std::size_t>* if_object() { return nullptr; }
virtual value_base_impl* if_value() const { return nullptr; }
value_base_impl() = default;
value_base_impl(const value_base_impl&) = default;
value_base_impl& operator=(const value_base_impl&) = default;
virtual ~value_base_impl() {}
};
#define MIGRAPHX_VALUE_GENERATE_BASE_TYPE(vt, cpp_type) \
struct vt##_value_holder : value_base_impl::share \
{ \
vt##_value_holder(cpp_type d) : data(std::move(d)) {} \
virtual value::type_t get_type() { return value::vt##_type; } \
virtual const cpp_type* if_##vt() const { return &data; } \
cpp_type data; \
};
MIGRAPHX_VISIT_VALUE_TYPES(MIGRAPHX_VALUE_GENERATE_BASE_TYPE)
struct array_value_holder : value_base_impl::derive<array_value_holder>
{
array_value_holder() {}
array_value_holder(std::vector<value> d) : data(std::move(d)) {}
virtual value::type_t get_type() { return value::array_type; }
virtual std::vector<value>* if_array() { return &data; }
std::vector<value> data;
};
struct object_value_holder : value_base_impl::derive<object_value_holder>
{
object_value_holder() {}
object_value_holder(std::vector<value> d, std::unordered_map<std::string, std::size_t> l)
: data(std::move(d)), lookup(std::move(l))
{
}
virtual value::type_t get_type() { return value::object_type; }
virtual std::vector<value>* if_array() { return &data; }
virtual std::unordered_map<std::string, std::size_t>* if_object() { return &lookup; }
std::vector<value> data;
std::unordered_map<std::string, std::size_t> lookup;
};
value::value(const value& rhs) : x(rhs.x ? rhs.x->clone() : nullptr), key(rhs.key) {}
value& value::operator=(value rhs)
{
std::swap(rhs.x, x);
if(not rhs.key.empty())
std::swap(rhs.key, key);
return *this;
}
void set_vector(std::shared_ptr<value_base_impl>& x,
const std::vector<value>& v,
bool array_on_empty = true)
{
if(v.empty())
{
if(array_on_empty)
x = std::make_shared<array_value_holder>();
else
x = std::make_shared<object_value_holder>();
return;
}
if(v.front().get_key().empty())
{
x = std::make_shared<array_value_holder>(v);
}
else
{
std::unordered_map<std::string, std::size_t> lookup;
std::size_t i = 0;
for(auto&& e : v)
{
lookup[e.get_key()] = i;
i++;
}
x = std::make_shared<object_value_holder>(v, lookup);
}
}
value::value(const std::initializer_list<value>& i) : x(nullptr)
{
if(i.size() == 2 and i.begin()->is_string())
{
key = i.begin()->get_string();
auto r = (i.begin() + 1)->x;
x = r ? r->clone() : nullptr;
return;
}
set_vector(x, std::vector<value>(i.begin(), i.end()));
}
value::value(const std::vector<value>& v, bool array_on_empty) : x(nullptr)
{
set_vector(x, v, array_on_empty);
}
value::value(const std::unordered_map<std::string, value>& m)
: value(std::vector<value>(m.begin(), m.end()), false)
{
}
value::value(const std::string& pkey, const std::vector<value>& v, bool array_on_empty)
: x(nullptr), key(pkey)
{
set_vector(x, v, array_on_empty);
}
value::value(const std::string& pkey, const std::unordered_map<std::string, value>& m)
: value(pkey, std::vector<value>(m.begin(), m.end()), false)
{
}
value::value(const std::string& pkey, std::nullptr_t) : x(nullptr), key(pkey) {}
value::value(const std::string& pkey, const value& rhs)
: x(rhs.x ? rhs.x->clone() : nullptr), key(pkey)
{
}
value::value(const char* i) : value(std::string(i)) {}
#define MIGRAPHX_VALUE_GENERATE_DEFINE_METHODS(vt, cpp_type) \
value::value(cpp_type i) : x(std::make_shared<vt##_value_holder>(std::move(i))) {} \
value::value(const std::string& pkey, cpp_type i) \
: x(std::make_shared<vt##_value_holder>(std::move(i))), key(pkey) \
{ \
} \
value& value::operator=(cpp_type rhs) \
{ \
x = std::make_shared<vt##_value_holder>(std::move(rhs)); \
return *this; \
} \
bool value::is_##vt() const { return x ? x->get_type() == vt##_type : false; } \
const cpp_type& value::get_##vt() const \
{ \
auto* r = this->if_##vt(); \
assert(r); \
return *r; \
} \
const cpp_type* value::if_##vt() const { return x ? x->if_##vt() : nullptr; }
MIGRAPHX_VISIT_VALUE_TYPES(MIGRAPHX_VALUE_GENERATE_DEFINE_METHODS)
bool value::is_array() const { return x ? x->get_type() == array_type : false; }
const std::vector<value>& value::value::get_array() const
{
auto* r = this->if_array();
assert(r);
return *r;
}
const std::vector<value>* value::if_array() const { return x ? x->if_array() : nullptr; }
bool value::is_object() const { return x ? x->get_type() == object_type : false; }
const std::vector<value>& value::get_object() const
{
auto* r = this->if_object();
assert(r);
return *r;
}
const std::vector<value>* value::if_object() const
{
auto* r = x ? x->if_array() : nullptr;
assert(r == nullptr or
std::none_of(r->begin(), r->end(), [](auto&& v) { return v.get_key().empty(); }));
return r;
}
bool value::is_null() const { return x == nullptr; }
const std::string& value::get_key() const { return key; }
std::vector<value>* if_array_impl(const std::shared_ptr<value_base_impl>& x)
{
if(!x)
return nullptr;
return x->if_array();
}
std::vector<value>& get_array_impl(const std::shared_ptr<value_base_impl>& x)
{
auto* a = if_array_impl(x);
assert(a);
return *a;
}
value* find_impl(const std::shared_ptr<value_base_impl>& x, const std::string& key)
{
auto* a = if_array_impl(x);
if(a == nullptr)
return nullptr;
auto* lookup = x->if_object();
if(lookup == nullptr)
return nullptr;
auto it = lookup->find(key);
if(it == lookup->end())
return a->data() + a->size();
return std::addressof((*a)[it->second]);
}
value* value::find(const std::string& pkey) { return find_impl(x, pkey); }
const value* value::find(const std::string& pkey) const { return find_impl(x, pkey); }
bool value::contains(const std::string& pkey) const
{
auto it = find(pkey);
if(it == nullptr)
return false;
if(it == end())
return false;
return true;
}
std::size_t value::size() const
{
auto* a = if_array_impl(x);
if(a == nullptr)
return 0;
return a->size();
}
bool value::empty() const { return size() == 0; }
const value* value::data() const
{
auto* a = if_array_impl(x);
if(a == nullptr)
return nullptr;
return a->data();
}
value* value::data()
{
auto* a = if_array_impl(x);
if(a == nullptr)
return nullptr;
return a->data();
}
value* value::begin()
{
// cppcheck-suppress assertWithSideEffect
assert(data() or empty());
return data();
}
const value* value::begin() const
{
assert(data() or empty());
return data();
}
value* value::end() { return begin() + size(); }
const value* value::end() const { return begin() + size(); }
value& value::front() { return *begin(); }
const value& value::front() const { return *begin(); }
value& value::back() { return *std::prev(end()); }
const value& value::back() const { return *std::prev(end()); }
value& value::at(std::size_t i)
{
auto* a = if_array_impl(x);
if(a == nullptr)
MIGRAPHX_THROW("Not an array");
return a->at(i);
}
const value& value::at(std::size_t i) const
{
auto* a = if_array_impl(x);
if(a == nullptr)
MIGRAPHX_THROW("Not an array");
return a->at(i);
}
value& value::at(const std::string& pkey)
{
auto* r = find(pkey);
if(r == nullptr)
MIGRAPHX_THROW("Not an object");
if(r == end())
MIGRAPHX_THROW("Key not found");
return *r;
}
const value& value::at(const std::string& pkey) const
{
auto* r = find(pkey);
if(r == nullptr)
MIGRAPHX_THROW("Not an object");
if(r == end())
MIGRAPHX_THROW("Key not found");
return *r;
}
value& value::operator[](std::size_t i) { return *(begin() + i); }
const value& value::operator[](std::size_t i) const { return *(begin() + i); }
value& value::operator[](const std::string& pkey) { return *emplace(pkey, nullptr).first; }
std::pair<value*, bool> value::insert(const value& v)
{
if(v.key.empty())
{
if(!x)
x = std::make_shared<array_value_holder>();
get_array_impl(x).push_back(v);
assert(this->if_array());
return std::make_pair(&back(), true);
}
else
{
if(!x)
x = std::make_shared<object_value_holder>();
auto p = x->if_object()->emplace(v.key, get_array_impl(x).size());
if(p.second)
get_array_impl(x).push_back(v);
assert(this->if_object());
return std::make_pair(&get_array_impl(x)[p.first->second], p.second);
}
}
value* value::insert(const value* pos, const value& v)
{
assert(v.key.empty());
if(!x)
x = std::make_shared<array_value_holder>();
auto&& a = get_array_impl(x);
auto it = a.insert(a.begin() + (pos - begin()), v);
return std::addressof(*it);
}
value value::without_key() const
{
value result = *this;
result.key = "";
return result;
}
value value::with_key(const std::string& pkey) const
{
value result = *this;
result.key = pkey;
return result;
}
template <class F, class T, class U, class Common = typename std::common_type<T, U>::type>
auto compare_common_impl(
rank<1>, F f, const std::string& keyx, const T& x, const std::string& keyy, const U& y)
{
return f(std::forward_as_tuple(keyx, Common(x)), std::forward_as_tuple(keyy, Common(y)));
}
template <class F>
auto compare_common_impl(
rank<1>, F f, const std::string& keyx, std::nullptr_t, const std::string& keyy, std::nullptr_t)
{
return f(std::forward_as_tuple(keyx, 0), std::forward_as_tuple(keyy, 0));
}
template <class F, class T, class U>
auto compare_common_impl(rank<0>, F, const std::string&, const T&, const std::string&, const U&)
{
return false;
}
template <class F>
bool compare(const value& x, const value& y, F f)
{
bool result = false;
x.visit([&](auto&& a) {
y.visit([&](auto&& b) {
result = compare_common_impl(rank<1>{}, f, x.get_key(), a, y.get_key(), b);
});
});
return result;
}
value::type_t value::get_type() const
{
if(!x)
return null_type;
return x->get_type();
}
bool operator==(const value& x, const value& y)
{
if(x.get_type() != y.get_type())
return false;
return compare(x, y, std::equal_to<>{});
}
bool operator!=(const value& x, const value& y) { return !(x == y); }
bool operator<(const value& x, const value& y) { return compare(x, y, std::less<>{}); }
bool operator<=(const value& x, const value& y) { return x == y or x < y; }
bool operator>(const value& x, const value& y) { return y < x; }
bool operator>=(const value& x, const value& y) { return x == y or x > y; }
template <class T>
void print_value(std::ostream& os, const T& x)
{
os << x;
}
template <class T, class U>
void print_value(std::ostream& os, const std::pair<T, U>& x)
{
os << x.first;
os << ": ";
print_value(os, x.second);
}
void print_value(std::ostream& os, const std::nullptr_t&) { os << "null"; }
void print_value(std::ostream& os, const std::vector<value>& x)
{
os << "{";
os << to_string_range(x);
os << "}";
}
std::ostream& operator<<(std::ostream& os, const value& d)
{
d.visit([&](auto&& y) { print_value(os, y); });
return os;
}
void value::debug_print() const { std::cout << *this << std::endl; }
} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx
#include <migraphx/literal.hpp> #include <migraphx/literal.hpp>
#include <migraphx/serialize.hpp>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include "test.hpp" #include "test.hpp"
...@@ -119,4 +120,36 @@ TEST_CASE(literal_visit_empty) ...@@ -119,4 +120,36 @@ TEST_CASE(literal_visit_empty)
EXPECT(test::throws([&] { x.visit_at([](auto) {}); })); EXPECT(test::throws([&] { x.visit_at([](auto) {}); }));
} }
TEST_CASE(value_literal)
{
migraphx::shape s{migraphx::shape::int64_type, {3}};
migraphx::literal l1{s, {1, 2, 3}};
auto v1 = migraphx::to_value(l1);
migraphx::literal l2{1};
auto v2 = migraphx::to_value(l2);
EXPECT(v1 != v2);
auto l3 = migraphx::from_value<migraphx::literal>(v1);
EXPECT(l3 == l1);
auto l4 = migraphx::from_value<migraphx::literal>(v2);
EXPECT(l4 == l2);
}
TEST_CASE(value_argument)
{
migraphx::shape s{migraphx::shape::int64_type, {3}};
migraphx::literal l1{s, {1, 2, 3}};
auto a1 = l1.get_argument();
auto v1 = migraphx::to_value(a1);
migraphx::literal l2{1};
auto a2 = l2.get_argument();
auto v2 = migraphx::to_value(a2);
EXPECT(v1 != v2);
auto a3 = migraphx::from_value<migraphx::argument>(v1);
EXPECT(a3 == a1);
auto a4 = migraphx::from_value<migraphx::argument>(v2);
EXPECT(a4 == a2);
}
int main(int argc, const char* argv[]) { test::run(argc, argv); } int main(int argc, const char* argv[]) { test::run(argc, argv); }
#include <migraphx/serialize.hpp>
#include <migraphx/functional.hpp>
#include <test.hpp>
struct empty_type
{
};
struct reflectable_type
{
enum simple_enum
{
simple1,
simple2,
simple3
};
enum class class_enum
{
class1,
class2,
class3
};
std::vector<std::size_t> ints = {};
std::string name = "";
float fvalue = 0.0;
empty_type et{};
simple_enum se = simple1;
class_enum ce = class_enum::class1;
struct nested_type
{
int value;
template <class Self, class F>
static auto reflect(Self& self, F f)
{
return migraphx::pack(f(self.value, "value"));
}
};
std::vector<nested_type> nested_types = {};
template <class Self, class F>
static auto reflect(Self& self, F f)
{
return migraphx::pack(f(self.ints, "ints"),
f(self.name, "name"),
f(self.fvalue, "fvalue"),
f(self.et, "et"),
f(self.se, "se"),
f(self.ce, "ce"),
f(self.nested_types, "nested_types"));
}
};
TEST_CASE(serialize_reflectable_type)
{
reflectable_type t1{{1, 2},
"hello",
1.0,
{},
reflectable_type::simple1,
reflectable_type::class_enum::class2,
{{1}, {2}}};
migraphx::value v1 = migraphx::to_value(t1);
reflectable_type t2 = migraphx::from_value<reflectable_type>(v1);
migraphx::value v2 = migraphx::to_value(t2);
migraphx::value v3 = migraphx::to_value(reflectable_type{});
EXPECT(v1 == v2);
EXPECT(v1 != v3);
EXPECT(v2 != v3);
}
int main(int argc, const char* argv[]) { test::run(argc, argv); }
#include <migraphx/shape.hpp> #include <migraphx/shape.hpp>
#include <migraphx/serialize.hpp>
#include <array> #include <array>
#include <algorithm> #include <algorithm>
#include <numeric> #include <numeric>
...@@ -370,4 +371,19 @@ TEST_CASE(test_shape4_nonpacked) ...@@ -370,4 +371,19 @@ TEST_CASE(test_shape4_nonpacked)
EXPECT(s.index(s.elements() - 1) == 469273); EXPECT(s.index(s.elements() - 1) == 469273);
} }
TEST_CASE(test_serialize)
{
migraphx::shape s1{migraphx::shape::float_type, {100, 32, 8, 8}};
auto v1 = migraphx::to_value(s1);
migraphx::shape s2{migraphx::shape::uint64_type, {2, 2}};
auto v2 = migraphx::to_value(s2);
EXPECT(v1 != v2);
auto s3 = migraphx::from_value<migraphx::shape>(v1);
EXPECT(s3 == s1);
auto s4 = migraphx::from_value<migraphx::shape>(v2);
EXPECT(s4 == s2);
EXPECT(s3 != s4);
}
int main(int argc, const char* argv[]) { test::run(argc, argv); } int main(int argc, const char* argv[]) { test::run(argc, argv); }
#include <migraphx/value.hpp>
#include <migraphx/float_equal.hpp>
#include <test.hpp>
TEST_CASE(value_default_construct)
{
migraphx::value v;
EXPECT(v.is_null());
EXPECT(v.get_key().empty());
}
TEST_CASE(value_construct_int1)
{
EXPECT(migraphx::value(1).is_int64());
migraphx::value v(1);
EXPECT(v.is_int64());
EXPECT(v.get_int64() == 1);
EXPECT(v.get_key().empty());
}
TEST_CASE(value_construct_int2)
{
migraphx::value v = 1;
EXPECT(v.is_int64());
EXPECT(v.get_int64() == 1);
EXPECT(v.get_key().empty());
}
TEST_CASE(value_construct_string)
{
migraphx::value v = "one";
EXPECT(v.is_string());
EXPECT(v.get_string() == "one");
EXPECT(v.get_key().empty());
}
TEST_CASE(value_construct_float)
{
migraphx::value v = 1.0;
EXPECT(v.is_float());
EXPECT(migraphx::float_equal(v.get_float(), 1.0));
EXPECT(v.get_key().empty());
}
TEST_CASE(value_construct_bool)
{
migraphx::value v = true;
EXPECT(v.is_bool());
EXPECT(v.get_bool() == true);
EXPECT(v.get_key().empty());
}
TEST_CASE(value_construct_empty_object)
{
migraphx::value v = migraphx::value::object{};
EXPECT(v.is_object());
EXPECT(v.get_object().empty());
EXPECT(v.get_key().empty());
}
TEST_CASE(value_construct_empty_array)
{
migraphx::value v = migraphx::value::array{};
EXPECT(v.is_array());
EXPECT(v.get_array().empty());
EXPECT(v.get_key().empty());
}
TEST_CASE(value_assign_int)
{
migraphx::value v;
v = 0;
EXPECT(v.is_int64());
EXPECT(v.get_int64() == 0);
EXPECT(v.get_key().empty());
}
TEST_CASE(value_copy_construct)
{
migraphx::value v1(1);
migraphx::value v2 = v1; // NOLINT
EXPECT(v1 == v2);
}
TEST_CASE(value_copy_assign)
{
migraphx::value v1(1);
migraphx::value v2;
v2 = v1;
EXPECT(v1 == v2);
}
TEST_CASE(value_reassign)
{
migraphx::value v1(1);
migraphx::value v2 = v1;
v1 = 2;
EXPECT(v1 != v2);
}
TEST_CASE(value_copy_assign_key)
{
migraphx::value v1("key", 1);
migraphx::value v2;
v2 = v1;
EXPECT(v2.get_key() == "key");
EXPECT(v1 == v2);
}
TEST_CASE(value_copy_assign_keyless)
{
migraphx::value v1(1);
migraphx::value v2("key", nullptr);
v2 = v1;
EXPECT(v2.get_key() == "key");
EXPECT(v1 != v2);
EXPECT(v1.without_key() == v2.without_key());
}
TEST_CASE(value_construct_array)
{
migraphx::value v = {1, 2, 3};
EXPECT(v.is_array());
EXPECT(v.get_array().size() == 3);
EXPECT(v.size() == 3);
EXPECT(not v.empty());
EXPECT(v.data() != nullptr);
EXPECT(v.front().is_int64());
EXPECT(v.front() == migraphx::value(1));
EXPECT(v[1] == migraphx::value(2));
EXPECT(v.at(1) == migraphx::value(2));
EXPECT(v.back() == migraphx::value(3));
EXPECT(test::throws([&] { v.at("???"); }));
[=] {
EXPECT(v.data() != nullptr);
EXPECT(v.front().is_int64());
EXPECT(v.front() == migraphx::value(1));
EXPECT(v[1] == migraphx::value(2));
EXPECT(v.at(1) == migraphx::value(2));
EXPECT(v.back() == migraphx::value(3));
}();
}
TEST_CASE(value_insert_array)
{
migraphx::value v;
v.insert(v.end(), 1);
v.insert(v.end(), 2);
v.insert(v.end(), 3);
EXPECT(v.is_array());
EXPECT(v.get_array().size() == 3);
EXPECT(v.size() == 3);
EXPECT(not v.empty());
EXPECT(v.data() != nullptr);
EXPECT(v.front().is_int64());
EXPECT(v.front() == migraphx::value(1));
EXPECT(v[1] == migraphx::value(2));
EXPECT(v.at(1) == migraphx::value(2));
EXPECT(v.back() == migraphx::value(3));
}
TEST_CASE(value_key_array)
{
std::vector<migraphx::value> values = {1, 2, 3};
migraphx::value v("key", values);
EXPECT(v.is_array());
EXPECT(v.get_key() == "key");
EXPECT(v.get_array().size() == 3);
EXPECT(v.size() == 3);
EXPECT(not v.empty());
EXPECT(v.data() != nullptr);
EXPECT(v.front().is_int64());
EXPECT(v.front() == migraphx::value(1));
EXPECT(v[1] == migraphx::value(2));
EXPECT(v.at(1) == migraphx::value(2));
EXPECT(v.back() == migraphx::value(3));
}
TEST_CASE(value_key_array_empty)
{
std::vector<migraphx::value> values{};
migraphx::value v("key", values);
EXPECT(v.is_array());
EXPECT(v.get_key() == "key");
EXPECT(v.get_array().size() == 0);
EXPECT(v.size() == 0);
EXPECT(v.empty());
}
TEST_CASE(value_construct_key_int1)
{
migraphx::value v("one", 1);
EXPECT(v.is_int64());
EXPECT(v.get_int64() == 1);
EXPECT(v.get_key() == "one");
}
TEST_CASE(value_construct_key_int2)
{
migraphx::value v = {"one", 1};
EXPECT(v.is_int64());
EXPECT(v.get_int64() == 1);
EXPECT(v.get_key() == "one");
}
TEST_CASE(value_construct_key_pair)
{
migraphx::value v = std::make_pair("one", 1);
EXPECT(v.is_int64());
EXPECT(v.get_int64() == 1);
EXPECT(v.get_key() == "one");
}
TEST_CASE(value_construct_object)
{
migraphx::value v = {{"one", 1}, {"two", migraphx::value(2)}, {"three", 3}};
EXPECT(v.is_object());
EXPECT(v.get_object().size() == 3);
EXPECT(v.size() == 3);
EXPECT(not v.empty());
EXPECT(v.data() != nullptr);
EXPECT(v.front().is_int64());
EXPECT(v.front().get_int64() == 1);
EXPECT(v.front().get_key() == "one");
EXPECT(v[1].is_int64());
EXPECT(v[1].get_int64() == 2);
EXPECT(v[1].get_key() == "two");
EXPECT(v.back().is_int64());
EXPECT(v.back().get_int64() == 3);
EXPECT(v.back().get_key() == "three");
EXPECT(v.contains("one"));
EXPECT(v.contains("two"));
EXPECT(v.contains("three"));
EXPECT(not v.contains("four"));
EXPECT(v.at("one").is_int64());
EXPECT(v.at("one").get_int64() == 1);
EXPECT(v.at("one").get_key() == "one");
EXPECT(v.at("two").is_int64());
EXPECT(v.at("two").get_int64() == 2);
EXPECT(v.at("two").get_key() == "two");
EXPECT(v.at("three").is_int64());
EXPECT(v.at("three").get_int64() == 3);
EXPECT(v.at("three").get_key() == "three");
EXPECT(v["one"].is_int64());
EXPECT(v["one"].get_int64() == 1);
EXPECT(v["one"].get_key() == "one");
EXPECT(v["two"].is_int64());
EXPECT(v["two"].get_int64() == 2);
EXPECT(v["two"].get_key() == "two");
EXPECT(v["three"].is_int64());
EXPECT(v["three"].get_int64() == 3);
EXPECT(v["three"].get_key() == "three");
}
TEST_CASE(value_key_object)
{
std::unordered_map<std::string, migraphx::value> values = {
{"one", 1}, {"two", migraphx::value(2)}, {"three", 3}};
migraphx::value v("key", values);
EXPECT(v.get_key() == "key");
EXPECT(v.is_object());
EXPECT(v.get_object().size() == 3);
EXPECT(v.size() == 3);
EXPECT(not v.empty());
EXPECT(v.data() != nullptr);
EXPECT(v.contains("one"));
EXPECT(v.contains("two"));
EXPECT(v.contains("three"));
EXPECT(not v.contains("four"));
EXPECT(v.at("one").is_int64());
EXPECT(v.at("one").get_int64() == 1);
EXPECT(v.at("one").get_key() == "one");
EXPECT(v.at("two").is_int64());
EXPECT(v.at("two").get_int64() == 2);
EXPECT(v.at("two").get_key() == "two");
EXPECT(v.at("three").is_int64());
EXPECT(v.at("three").get_int64() == 3);
EXPECT(v.at("three").get_key() == "three");
EXPECT(v["one"].is_int64());
EXPECT(v["one"].get_int64() == 1);
EXPECT(v["one"].get_key() == "one");
EXPECT(v["two"].is_int64());
EXPECT(v["two"].get_int64() == 2);
EXPECT(v["two"].get_key() == "two");
EXPECT(v["three"].is_int64());
EXPECT(v["three"].get_int64() == 3);
EXPECT(v["three"].get_key() == "three");
}
TEST_CASE(value_key_object_empty)
{
std::unordered_map<std::string, migraphx::value> values{};
migraphx::value v("key", values);
EXPECT(v.get_key() == "key");
EXPECT(v.is_object());
EXPECT(v.get_object().size() == 0);
EXPECT(v.size() == 0);
EXPECT(v.empty());
EXPECT(not v.contains("one"));
}
TEST_CASE(value_bracket_object)
{
migraphx::value v;
v["one"] = 1;
v["two"] = migraphx::value(2);
v["three"] = 3;
EXPECT(v.is_object());
EXPECT(v.get_object().size() == 3);
EXPECT(v.size() == 3);
EXPECT(not v.empty());
EXPECT(v.data() != nullptr);
EXPECT(v.front().is_int64());
EXPECT(v.front().get_int64() == 1);
EXPECT(v.front().get_key() == "one");
EXPECT(v[1].is_int64());
EXPECT(v[1].get_int64() == 2);
EXPECT(v[1].get_key() == "two");
EXPECT(v.back().is_int64());
EXPECT(v.back().get_int64() == 3);
EXPECT(v.back().get_key() == "three");
EXPECT(v.contains("one"));
EXPECT(v.contains("two"));
EXPECT(v.contains("three"));
EXPECT(not v.contains("four"));
EXPECT(v.at("one").is_int64());
EXPECT(v.at("one").get_int64() == 1);
EXPECT(v.at("one").get_key() == "one");
EXPECT(v.at("two").is_int64());
EXPECT(v.at("two").get_int64() == 2);
EXPECT(v.at("two").get_key() == "two");
EXPECT(v.at("three").is_int64());
EXPECT(v.at("three").get_int64() == 3);
EXPECT(v.at("three").get_key() == "three");
}
TEST_CASE(value_insert_object)
{
migraphx::value v;
v.insert({"one", 1});
v.insert({"two", migraphx::value(2)});
v.insert({"three", 3});
EXPECT(v.is_object());
EXPECT(v.get_object().size() == 3);
EXPECT(v.size() == 3);
EXPECT(not v.empty());
EXPECT(v.data() != nullptr);
EXPECT(v.front().is_int64());
EXPECT(v.front().get_int64() == 1);
EXPECT(v.front().get_key() == "one");
EXPECT(v[1].is_int64());
EXPECT(v[1].get_int64() == 2);
EXPECT(v[1].get_key() == "two");
EXPECT(v.back().is_int64());
EXPECT(v.back().get_int64() == 3);
EXPECT(v.back().get_key() == "three");
EXPECT(v.contains("one"));
EXPECT(v.contains("two"));
EXPECT(v.contains("three"));
EXPECT(not v.contains("four"));
EXPECT(v.at("one").is_int64());
EXPECT(v.at("one").get_int64() == 1);
EXPECT(v.at("one").get_key() == "one");
EXPECT(v.at("two").is_int64());
EXPECT(v.at("two").get_int64() == 2);
EXPECT(v.at("two").get_key() == "two");
EXPECT(v.at("three").is_int64());
EXPECT(v.at("three").get_int64() == 3);
EXPECT(v.at("three").get_key() == "three");
EXPECT(v["one"].is_int64());
EXPECT(v["one"].get_int64() == 1);
EXPECT(v["one"].get_key() == "one");
EXPECT(v["two"].is_int64());
EXPECT(v["two"].get_int64() == 2);
EXPECT(v["two"].get_key() == "two");
EXPECT(v["three"].is_int64());
EXPECT(v["three"].get_int64() == 3);
EXPECT(v["three"].get_key() == "three");
}
TEST_CASE(value_emplace_object)
{
migraphx::value v;
v.emplace("one", 1);
v.emplace("two", migraphx::value(2));
v.emplace("three", 3);
EXPECT(v.is_object());
EXPECT(v.size() == 3);
EXPECT(not v.empty());
EXPECT(v.data() != nullptr);
EXPECT(v.front().is_int64());
EXPECT(v.front().get_int64() == 1);
EXPECT(v.front().get_key() == "one");
EXPECT(v[1].is_int64());
EXPECT(v[1].get_int64() == 2);
EXPECT(v[1].get_key() == "two");
EXPECT(v.back().is_int64());
EXPECT(v.back().get_int64() == 3);
EXPECT(v.back().get_key() == "three");
EXPECT(v.contains("one"));
EXPECT(v.contains("two"));
EXPECT(v.contains("three"));
EXPECT(not v.contains("four"));
EXPECT(v.at("one").is_int64());
EXPECT(v.at("one").get_int64() == 1);
EXPECT(v.at("one").get_key() == "one");
EXPECT(v.at("two").is_int64());
EXPECT(v.at("two").get_int64() == 2);
EXPECT(v.at("two").get_key() == "two");
EXPECT(v.at("three").is_int64());
EXPECT(v.at("three").get_int64() == 3);
EXPECT(v.at("three").get_key() == "three");
EXPECT(v["one"].is_int64());
EXPECT(v["one"].get_int64() == 1);
EXPECT(v["one"].get_key() == "one");
EXPECT(v["two"].is_int64());
EXPECT(v["two"].get_int64() == 2);
EXPECT(v["two"].get_key() == "two");
EXPECT(v["three"].is_int64());
EXPECT(v["three"].get_int64() == 3);
EXPECT(v["three"].get_key() == "three");
}
TEST_CASE(value_compare)
{
EXPECT(migraphx::value(1) == migraphx::value(1));
EXPECT(migraphx::value("key", 1) == migraphx::value("key", 1));
EXPECT(migraphx::value(1) != migraphx::value(2));
EXPECT(migraphx::value("key", 1) != migraphx::value("key", 2));
EXPECT(migraphx::value("key1", 1) != migraphx::value("key2", 1));
EXPECT(migraphx::value(1) < migraphx::value(2));
EXPECT(migraphx::value(1) <= migraphx::value(2));
EXPECT(migraphx::value(1) <= migraphx::value(1));
EXPECT(migraphx::value(2) > migraphx::value(1));
EXPECT(migraphx::value(2) >= migraphx::value(1));
EXPECT(migraphx::value(1) >= migraphx::value(1));
}
TEST_CASE(value_to_from_string)
{
migraphx::value v = "1";
EXPECT(v.to<std::string>() == "1");
EXPECT(v.to<int>() == 1);
EXPECT(migraphx::float_equal(v.to<float>(), 1.0));
}
TEST_CASE(value_to_from_int)
{
migraphx::value v = 1;
EXPECT(v.to<std::string>() == "1");
EXPECT(v.to<int>() == 1);
EXPECT(migraphx::float_equal(v.to<float>(), 1.0));
}
TEST_CASE(value_to_from_float)
{
migraphx::value v = 1.5;
EXPECT(v.to<std::string>() == "1.5");
EXPECT(v.to<int>() == 1);
EXPECT(migraphx::float_equal(v.to<float>(), 1.5));
}
TEST_CASE(value_to_from_pair)
{
migraphx::value v = {"one", 1};
EXPECT(bool{v.to<std::pair<std::string, std::string>>() ==
std::pair<std::string, std::string>("one", "1")});
EXPECT(bool{v.to<std::pair<std::string, int>>() == std::pair<std::string, int>("one", 1)});
EXPECT(
bool{v.to<std::pair<std::string, float>>() == std::pair<std::string, float>("one", 1.0)});
}
TEST_CASE(value_to_struct)
{
migraphx::value v = 1;
struct local
{
int i = 0;
local() = default;
local(int ii) : i(ii) {}
};
EXPECT(v.to<local>().i == 1);
}
TEST_CASE(value_to_error1)
{
migraphx::value v = {1, 2, 3};
EXPECT(test::throws([&] { v.to<int>(); }));
}
TEST_CASE(value_to_error2)
{
migraphx::value v = 1;
struct local
{
};
EXPECT(test::throws([&] { v.to<local>(); }));
}
TEST_CASE(value_to_error_parse)
{
migraphx::value v = "abc";
EXPECT(test::throws([&] { v.to<int>(); }));
}
TEST_CASE(value_to_vector)
{
migraphx::value v = {1, 2, 3};
std::vector<int> a = {1, 2, 3};
EXPECT(v.to_vector<int>() == a);
}
TEST_CASE(not_array)
{
migraphx::value v = 1;
EXPECT(v.size() == 0);
EXPECT(not v.contains("???"));
EXPECT(test::throws([&] { v.at(0); }));
EXPECT(test::throws([&] { v.at("???"); }));
EXPECT(v.data() == nullptr);
[=] {
EXPECT(test::throws([&] { v.at(0); }));
EXPECT(test::throws([&] { v.at("???"); }));
EXPECT(v.data() == nullptr);
}();
}
TEST_CASE(print)
{
std::stringstream ss;
migraphx::value v = {1, {{"one", 1}, {"two", 2}}, {1, 2}, {}};
ss << v;
EXPECT(ss.str() == "{1, {one: 1, two: 2}, {1, 2}, null}");
}
int main(int argc, const char* argv[]) { test::run(argc, argv); }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment