diff --git a/core/luajit/context.cc b/core/luajit/context.cc index 0313e18..b975ff4 100644 --- a/core/luajit/context.cc +++ b/core/luajit/context.cc @@ -41,6 +41,7 @@ void TaskContext::Push(const nf7::Value& v) noexcept { v.is() ? "real": v.is() ? "buffer": v.is() ? "object": + v.is() ? "data": "unknown"); return 1; }); diff --git a/iface/common/value.hh b/iface/common/value.hh index 43bb44e..2fde9c7 100644 --- a/iface/common/value.hh +++ b/iface/common/value.hh @@ -147,7 +147,13 @@ class Value final { std::shared_ptr pairs_; }; - using Variant = std::variant; + class Data { + public: + virtual ~Data() = default; + }; + using SharedData = std::shared_ptr; + + using Variant = std::variant; public: static Value MakeNull(Null v = {}) noexcept { return v; } @@ -217,6 +223,14 @@ class Value final { return MakeArray(v.begin(), v.end()); } + template + static Value MakeSharedData(Args&&... args) + try { + return Value {std::make_shared(std::forward(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 + std::shared_ptr data( + std::source_location loc = std::source_location::current()) const { + static_assert(std::is_base_of_v); + + const auto ret = std::dynamic_pointer_cast(as(loc)); + return nullptr != ret? ret: + throw Exception {"incompatible data type", loc}; + } + private: Variant var_; }; diff --git a/iface/common/value_test.cc b/iface/common/value_test.cc index ef19794..84b728f 100644 --- a/iface/common/value_test.cc +++ b/iface/common/value_test.cc @@ -4,6 +4,14 @@ #include +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()); @@ -11,6 +19,7 @@ TEST(Value, NullAsNull) { EXPECT_FALSE(v.is()); EXPECT_FALSE(v.is()); EXPECT_FALSE(v.is()); + EXPECT_FALSE(v.is()); } TEST(Value, NullAsInvalid) { const auto v = nf7::Value::MakeNull(); @@ -18,6 +27,7 @@ TEST(Value, NullAsInvalid) { EXPECT_THROW(v.as(), nf7::Exception); EXPECT_THROW(v.as(), nf7::Exception); EXPECT_THROW(v.as(), nf7::Exception); + EXPECT_THROW(v.as(), 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({})); EXPECT_NE(nf7::Value::MakeNull(), nf7::Value::MakeObject({})); + EXPECT_NE(nf7::Value::MakeNull(), nf7::Value::MakeSharedData()); } TEST(Value, IntegerAsInteger) { @@ -37,6 +48,7 @@ TEST(Value, IntegerAsInteger) { EXPECT_FALSE(v.is()); EXPECT_FALSE(v.is()); EXPECT_FALSE(v.is()); + EXPECT_FALSE(v.is()); EXPECT_EQ(v.as(), nf7::Value::Integer {777}); } @@ -46,6 +58,7 @@ TEST(Value, IntegerAsInvalid) { EXPECT_THROW(v.as(), nf7::Exception); EXPECT_THROW(v.as(), nf7::Exception); EXPECT_THROW(v.as(), nf7::Exception); + EXPECT_THROW(v.as(), 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({})); EXPECT_NE(nf7::Value::MakeInteger(666), nf7::Value::MakeObject({})); + EXPECT_NE(nf7::Value::MakeInteger(666), nf7::Value::MakeSharedData()); } TEST(Value, RealAsReal) { @@ -76,6 +90,7 @@ TEST(Value, RealAsReal) { EXPECT_TRUE(v.is()); EXPECT_FALSE(v.is()); EXPECT_FALSE(v.is()); + EXPECT_FALSE(v.is()); EXPECT_EQ(v.as(), nf7::Value::Real {777}); } @@ -85,6 +100,7 @@ TEST(Value, RealAsInvalid) { EXPECT_THROW(v.as(), nf7::Exception); EXPECT_THROW(v.as(), nf7::Exception); EXPECT_THROW(v.as(), nf7::Exception); + EXPECT_THROW(v.as(), 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({})); EXPECT_NE(nf7::Value::MakeReal(1), nf7::Value::MakeObject({})); + EXPECT_NE(nf7::Value::MakeReal(1), nf7::Value::MakeSharedData()); } TEST(Value, BufferAsBuffer) { @@ -114,6 +131,7 @@ TEST(Value, BufferAsBuffer) { EXPECT_FALSE(v.is()); EXPECT_TRUE(v.is()); EXPECT_FALSE(v.is()); + EXPECT_FALSE(v.is()); } TEST(Value, BufferAsInvalid) { const auto v = nf7::Value::MakeBuffer({}); @@ -121,6 +139,7 @@ TEST(Value, BufferAsInvalid) { EXPECT_THROW(v.as(), nf7::Exception); EXPECT_THROW(v.as(), nf7::Exception); EXPECT_THROW(v.as(), nf7::Exception); + EXPECT_THROW(v.as(), nf7::Exception); } TEST(Value, BufferEqual) { const auto v = nf7::Value::MakeBuffer({}); @@ -133,6 +152,7 @@ TEST(Value, BufferNotEqual) { EXPECT_NE(nf7::Value::MakeBuffer({}), nf7::Value::MakeBuffer({})); EXPECT_NE(nf7::Value::MakeBuffer({}), nf7::Value::MakeObject({})); + EXPECT_NE(nf7::Value::MakeBuffer({}), nf7::Value::MakeSharedData()); } TEST(Value, ObjectAsObject) { @@ -142,6 +162,7 @@ TEST(Value, ObjectAsObject) { EXPECT_FALSE(v.is()); EXPECT_FALSE(v.is()); EXPECT_TRUE(v.is()); + EXPECT_FALSE(v.is()); } TEST(Value, ObjectAsInvalid) { const auto v = nf7::Value::MakeObject({}); @@ -149,6 +170,7 @@ TEST(Value, ObjectAsInvalid) { EXPECT_THROW(v.as(), nf7::Exception); EXPECT_THROW(v.as(), nf7::Exception); EXPECT_THROW(v.as(), nf7::Exception); + EXPECT_THROW(v.as(), 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({})); EXPECT_NE(nf7::Value::MakeObject({}), nf7::Value::MakeObject({})); + EXPECT_NE(nf7::Value::MakeObject({}), nf7::Value::MakeSharedData()); +} + +TEST(Value, DataAsCompatibleData) { + const auto v = nf7::Value::MakeSharedData(); + EXPECT_FALSE(v.is()); + EXPECT_FALSE(v.is()); + EXPECT_FALSE(v.is()); + EXPECT_FALSE(v.is()); + EXPECT_FALSE(v.is()); + EXPECT_TRUE(v.is()); + + EXPECT_NE(v.data(), nullptr); +} +TEST(Value, DataAsIncompatibleData) { + const auto v = nf7::Value::MakeSharedData(); + EXPECT_THROW(v.data(), nf7::Exception); +} +TEST(Value, DataAsInvalid) { + const auto v = nf7::Value::MakeSharedData(); + EXPECT_THROW(v.as(), nf7::Exception); + EXPECT_THROW(v.as(), nf7::Exception); + EXPECT_THROW(v.as(), nf7::Exception); + EXPECT_THROW(v.as(), nf7::Exception); + EXPECT_THROW(v.as(), nf7::Exception); +} +TEST(Value, DataAsIncompatibleType) { + const auto v = nf7::Value::MakeSharedData(); + EXPECT_THROW(v.as(), nf7::Exception); + EXPECT_THROW(v.as(), nf7::Exception); + EXPECT_THROW(v.as(), nf7::Exception); + EXPECT_THROW(v.as(), nf7::Exception); + EXPECT_THROW(v.as(), nf7::Exception); +} +TEST(Value, DataEqual) { + const auto v = nf7::Value::MakeSharedData(); + EXPECT_EQ(v, v); +} +TEST(Value, DataNotEqual) { + const auto v = nf7::Value::MakeSharedData(); + 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({})); + EXPECT_NE(v, nf7::Value::MakeObject({})); + EXPECT_NE(v, nf7::Value::MakeSharedData()); + EXPECT_NE(v, nf7::Value::MakeSharedData()); } TEST(ValueBuffer, Make) {