allow nf7::Value to hold a custom data type

This commit is contained in:
falsycat 2023-10-01 17:07:05 +09:00
parent 1f3faa1dbb
commit b1a19b96ba
3 changed files with 97 additions and 1 deletions

View File

@ -41,6 +41,7 @@ void TaskContext::Push(const nf7::Value& v) noexcept {
v.is<nf7::Value::Real>() ? "real":
v.is<nf7::Value::Buffer>() ? "buffer":
v.is<nf7::Value::Object>() ? "object":
v.is<nf7::Value::SharedData>() ? "data":
"unknown");
return 1;
});

View File

@ -147,7 +147,13 @@ class Value final {
std::shared_ptr<const Pair[]> pairs_;
};
using Variant = std::variant<Null, Integer, Real, Buffer, Object>;
class Data {
public:
virtual ~Data() = default;
};
using SharedData = std::shared_ptr<Data>;
using Variant = std::variant<Null, Integer, Real, Buffer, Object, SharedData>;
public:
static Value MakeNull(Null v = {}) noexcept { return v; }
@ -217,6 +223,14 @@ class Value final {
return MakeArray(v.begin(), v.end());
}
template <typename T, typename... Args>
static Value MakeSharedData(Args&&... args)
try {
return Value {std::make_shared<T>(std::forward<Args>(args)...)};
} catch (const std::bad_alloc&) {
throw MemoryException {};
}
public:
Value() noexcept : var_(Null {}) { }
Value(Null v) noexcept : var_(v) { }
@ -226,6 +240,8 @@ class Value final {
Value(const Buffer& v) noexcept : var_(v) { }
Value(Object&& v) noexcept : var_(std::move(v)) { }
Value(const Object& v) noexcept : var_(v) { }
Value(SharedData&& v) noexcept : var_(std::move(v)) { }
Value(const SharedData& v) noexcept : var_(v) { }
public:
Value(const Value&) = default;
@ -277,6 +293,16 @@ class Value final {
throw Exception {"value is not a number", location};
}
template <typename T>
std::shared_ptr<T> data(
std::source_location loc = std::source_location::current()) const {
static_assert(std::is_base_of_v<Data, T>);
const auto ret = std::dynamic_pointer_cast<T>(as<SharedData>(loc));
return nullptr != ret? ret:
throw Exception {"incompatible data type", loc};
}
private:
Variant var_;
};

View File

@ -4,6 +4,14 @@
#include <gtest/gtest.h>
namespace {
class CustomData1 : public nf7::Value::Data { };
class CustomData2 : public nf7::Value::Data { };
} // namespace
TEST(Value, NullAsNull) {
const auto v = nf7::Value::MakeNull();
EXPECT_TRUE(v.is<nf7::Value::Null>());
@ -11,6 +19,7 @@ TEST(Value, NullAsNull) {
EXPECT_FALSE(v.is<nf7::Value::Real>());
EXPECT_FALSE(v.is<nf7::Value::Buffer>());
EXPECT_FALSE(v.is<nf7::Value::Object>());
EXPECT_FALSE(v.is<nf7::Value::SharedData>());
}
TEST(Value, NullAsInvalid) {
const auto v = nf7::Value::MakeNull();
@ -18,6 +27,7 @@ TEST(Value, NullAsInvalid) {
EXPECT_THROW(v.as<nf7::Value::Real>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Buffer>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Object>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::SharedData>(), nf7::Exception);
}
TEST(Value, NullEqual) {
EXPECT_EQ(nf7::Value::MakeNull(), nf7::Value::MakeNull());
@ -27,6 +37,7 @@ TEST(Value, NullNotEqual) {
EXPECT_NE(nf7::Value::MakeNull(), nf7::Value::MakeReal(0));
EXPECT_NE(nf7::Value::MakeNull(), nf7::Value::MakeBuffer<uint8_t>({}));
EXPECT_NE(nf7::Value::MakeNull(), nf7::Value::MakeObject({}));
EXPECT_NE(nf7::Value::MakeNull(), nf7::Value::MakeSharedData<CustomData1>());
}
TEST(Value, IntegerAsInteger) {
@ -37,6 +48,7 @@ TEST(Value, IntegerAsInteger) {
EXPECT_FALSE(v.is<nf7::Value::Real>());
EXPECT_FALSE(v.is<nf7::Value::Buffer>());
EXPECT_FALSE(v.is<nf7::Value::Object>());
EXPECT_FALSE(v.is<nf7::Value::SharedData>());
EXPECT_EQ(v.as<nf7::Value::Integer>(), nf7::Value::Integer {777});
}
@ -46,6 +58,7 @@ TEST(Value, IntegerAsInvalid) {
EXPECT_THROW(v.as<nf7::Value::Real>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Buffer>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Object>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::SharedData>(), nf7::Exception);
}
TEST(Value, IntegerAsValidNum) {
const nf7::Value v = nf7::Value::MakeInteger(777);
@ -66,6 +79,7 @@ TEST(Value, IntegerNotEqual) {
EXPECT_NE(nf7::Value::MakeInteger(666), nf7::Value::MakeReal(0));
EXPECT_NE(nf7::Value::MakeInteger(666), nf7::Value::MakeBuffer<uint8_t>({}));
EXPECT_NE(nf7::Value::MakeInteger(666), nf7::Value::MakeObject({}));
EXPECT_NE(nf7::Value::MakeInteger(666), nf7::Value::MakeSharedData<CustomData1>());
}
TEST(Value, RealAsReal) {
@ -76,6 +90,7 @@ TEST(Value, RealAsReal) {
EXPECT_TRUE(v.is<nf7::Value::Real>());
EXPECT_FALSE(v.is<nf7::Value::Buffer>());
EXPECT_FALSE(v.is<nf7::Value::Object>());
EXPECT_FALSE(v.is<nf7::Value::SharedData>());
EXPECT_EQ(v.as<nf7::Value::Real>(), nf7::Value::Real {777});
}
@ -85,6 +100,7 @@ TEST(Value, RealAsInvalid) {
EXPECT_THROW(v.as<nf7::Value::Integer>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Buffer>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Object>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::SharedData>(), nf7::Exception);
}
TEST(Value, RealAsValidNum) {
const auto v = nf7::Value::MakeReal(777);
@ -105,6 +121,7 @@ TEST(Value, RealNotEqual) {
EXPECT_NE(nf7::Value::MakeReal(1), nf7::Value::MakeInteger(1));
EXPECT_NE(nf7::Value::MakeReal(1), nf7::Value::MakeBuffer<uint8_t>({}));
EXPECT_NE(nf7::Value::MakeReal(1), nf7::Value::MakeObject({}));
EXPECT_NE(nf7::Value::MakeReal(1), nf7::Value::MakeSharedData<CustomData1>());
}
TEST(Value, BufferAsBuffer) {
@ -114,6 +131,7 @@ TEST(Value, BufferAsBuffer) {
EXPECT_FALSE(v.is<nf7::Value::Real>());
EXPECT_TRUE(v.is<nf7::Value::Buffer>());
EXPECT_FALSE(v.is<nf7::Value::Object>());
EXPECT_FALSE(v.is<nf7::Value::SharedData>());
}
TEST(Value, BufferAsInvalid) {
const auto v = nf7::Value::MakeBuffer<uint8_t>({});
@ -121,6 +139,7 @@ TEST(Value, BufferAsInvalid) {
EXPECT_THROW(v.as<nf7::Value::Integer>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Real>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Object>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::SharedData>(), nf7::Exception);
}
TEST(Value, BufferEqual) {
const auto v = nf7::Value::MakeBuffer<uint8_t>({});
@ -133,6 +152,7 @@ TEST(Value, BufferNotEqual) {
EXPECT_NE(nf7::Value::MakeBuffer<uint8_t>({}),
nf7::Value::MakeBuffer<uint8_t>({}));
EXPECT_NE(nf7::Value::MakeBuffer<uint8_t>({}), nf7::Value::MakeObject({}));
EXPECT_NE(nf7::Value::MakeBuffer<uint8_t>({}), nf7::Value::MakeSharedData<CustomData1>());
}
TEST(Value, ObjectAsObject) {
@ -142,6 +162,7 @@ TEST(Value, ObjectAsObject) {
EXPECT_FALSE(v.is<nf7::Value::Real>());
EXPECT_FALSE(v.is<nf7::Value::Buffer>());
EXPECT_TRUE(v.is<nf7::Value::Object>());
EXPECT_FALSE(v.is<nf7::Value::SharedData>());
}
TEST(Value, ObjectAsInvalid) {
const auto v = nf7::Value::MakeObject({});
@ -149,6 +170,7 @@ TEST(Value, ObjectAsInvalid) {
EXPECT_THROW(v.as<nf7::Value::Integer>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Real>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Buffer>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::SharedData>(), nf7::Exception);
}
TEST(Value, ObjectEqual) {
const auto v = nf7::Value::MakeObject({});
@ -160,6 +182,53 @@ TEST(Value, ObjectNotEqual) {
EXPECT_NE(nf7::Value::MakeObject({}), nf7::Value::MakeReal(0));
EXPECT_NE(nf7::Value::MakeObject({}), nf7::Value::MakeBuffer<uint8_t>({}));
EXPECT_NE(nf7::Value::MakeObject({}), nf7::Value::MakeObject({}));
EXPECT_NE(nf7::Value::MakeObject({}), nf7::Value::MakeSharedData<CustomData1>());
}
TEST(Value, DataAsCompatibleData) {
const auto v = nf7::Value::MakeSharedData<CustomData1>();
EXPECT_FALSE(v.is<nf7::Value::Null>());
EXPECT_FALSE(v.is<nf7::Value::Integer>());
EXPECT_FALSE(v.is<nf7::Value::Real>());
EXPECT_FALSE(v.is<nf7::Value::Buffer>());
EXPECT_FALSE(v.is<nf7::Value::Object>());
EXPECT_TRUE(v.is<nf7::Value::SharedData>());
EXPECT_NE(v.data<CustomData1>(), nullptr);
}
TEST(Value, DataAsIncompatibleData) {
const auto v = nf7::Value::MakeSharedData<CustomData1>();
EXPECT_THROW(v.data<CustomData2>(), nf7::Exception);
}
TEST(Value, DataAsInvalid) {
const auto v = nf7::Value::MakeSharedData<CustomData1>();
EXPECT_THROW(v.as<nf7::Value::Null>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Integer>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Real>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Buffer>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Object>(), nf7::Exception);
}
TEST(Value, DataAsIncompatibleType) {
const auto v = nf7::Value::MakeSharedData<CustomData1>();
EXPECT_THROW(v.as<nf7::Value::Null>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Integer>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Real>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Buffer>(), nf7::Exception);
EXPECT_THROW(v.as<nf7::Value::Object>(), nf7::Exception);
}
TEST(Value, DataEqual) {
const auto v = nf7::Value::MakeSharedData<CustomData1>();
EXPECT_EQ(v, v);
}
TEST(Value, DataNotEqual) {
const auto v = nf7::Value::MakeSharedData<CustomData1>();
EXPECT_NE(v, nf7::Value::MakeNull());
EXPECT_NE(v, nf7::Value::MakeInteger(0));
EXPECT_NE(v, nf7::Value::MakeReal(0));
EXPECT_NE(v, nf7::Value::MakeBuffer<uint8_t>({}));
EXPECT_NE(v, nf7::Value::MakeObject({}));
EXPECT_NE(v, nf7::Value::MakeSharedData<CustomData1>());
EXPECT_NE(v, nf7::Value::MakeSharedData<CustomData2>());
}
TEST(ValueBuffer, Make) {