api更新
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
#include <structive/property/property.hpp>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <semaphore>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <typeinfo>
|
||||
using namespace structive;
|
||||
#define REQUIRE(expression) do { if (!(expression)) { std::fprintf(stderr, "REQUIRE failed: %s:%d: %s\n", __FILE__, __LINE__, #expression); std::abort(); } } while (false)
|
||||
struct Runtime_Device : Property_Object<Runtime_Device> {
|
||||
int value{10};
|
||||
int serial{7};
|
||||
int command{0};
|
||||
};
|
||||
template <>
|
||||
struct structive::Type_Descriptor<Runtime_Device> {
|
||||
static auto get() {
|
||||
return object<Runtime_Device>(
|
||||
synchronization(sync_all_independent),
|
||||
field<&Runtime_Device::value>(key<"value">),
|
||||
field<&Runtime_Device::serial>(key<"serial">, read_only),
|
||||
field<&Runtime_Device::command>(key<"command">, write_only)
|
||||
);
|
||||
}
|
||||
};
|
||||
struct Runtime_Read_Capture {
|
||||
std::size_t calls{};
|
||||
std::size_t index{};
|
||||
std::string key;
|
||||
const std::type_info* type{};
|
||||
int value{};
|
||||
};
|
||||
static void capture_int(void* context, std::size_t index, std::string_view key, const std::type_info& type, const void* value) {
|
||||
auto& capture = *static_cast<Runtime_Read_Capture*>(context);
|
||||
++capture.calls;
|
||||
capture.index = index;
|
||||
capture.key = key;
|
||||
capture.type = &type;
|
||||
capture.value = *static_cast<const int*>(value);
|
||||
}
|
||||
template <class Object>
|
||||
concept Can_Read_Command = requires(const Object& object) {
|
||||
object.template read<"command">();
|
||||
};
|
||||
template <class Object>
|
||||
concept Can_Write_Serial = requires(Object& object) {
|
||||
object.template write<"serial">(1);
|
||||
};
|
||||
static void test_runtime_metadata_and_results() {
|
||||
Runtime_Device device;
|
||||
Property_Object_Base& erased = device;
|
||||
static_assert(!Can_Read_Command<Runtime_Device>);
|
||||
static_assert(!Can_Write_Serial<Runtime_Device>);
|
||||
REQUIRE(erased.runtime_object_type() == typeid(Runtime_Device));
|
||||
REQUIRE(erased.runtime_property_count() == 3);
|
||||
Runtime_Read_Capture capture;
|
||||
REQUIRE(erased.runtime_read("value", &capture, &capture_int) == Runtime_Access_Result::ok);
|
||||
REQUIRE(capture.calls == 1);
|
||||
REQUIRE(capture.index == 0);
|
||||
REQUIRE(capture.key == "value");
|
||||
REQUIRE(capture.type != nullptr && *capture.type == typeid(int));
|
||||
REQUIRE(capture.value == 10);
|
||||
capture = {};
|
||||
REQUIRE(erased.runtime_read("serial", &capture, &capture_int) == Runtime_Access_Result::ok);
|
||||
REQUIRE(capture.calls == 1);
|
||||
REQUIRE(capture.index == 1);
|
||||
REQUIRE(capture.value == 7);
|
||||
capture = {};
|
||||
REQUIRE(erased.runtime_read("command", &capture, &capture_int) == Runtime_Access_Result::not_readable);
|
||||
REQUIRE(capture.calls == 0);
|
||||
REQUIRE(erased.runtime_read("missing", &capture, &capture_int) == Runtime_Access_Result::unknown_property);
|
||||
REQUIRE(capture.calls == 0);
|
||||
int value = 20;
|
||||
REQUIRE(erased.runtime_write("value", typeid(int), &value) == Runtime_Access_Result::ok);
|
||||
REQUIRE(device.value == 20);
|
||||
int command = 5;
|
||||
REQUIRE(erased.runtime_write("command", typeid(int), &command) == Runtime_Access_Result::ok);
|
||||
REQUIRE(device.command == 5);
|
||||
REQUIRE(erased.runtime_write("serial", typeid(int), &value) == Runtime_Access_Result::not_writable);
|
||||
double wrong_type = 1.0;
|
||||
REQUIRE(erased.runtime_write("value", typeid(double), &wrong_type) == Runtime_Access_Result::type_mismatch);
|
||||
REQUIRE(erased.runtime_write("missing", typeid(int), &value) == Runtime_Access_Result::unknown_property);
|
||||
}
|
||||
static void test_runtime_access_uses_managed_synchronization() {
|
||||
Runtime_Device device;
|
||||
Property_Object_Base& erased = device;
|
||||
std::binary_semaphore read_done{0};
|
||||
std::jthread reader;
|
||||
{
|
||||
auto guard = device.lock_unique<&Runtime_Device::value>();
|
||||
reader = std::jthread([&] {
|
||||
Runtime_Read_Capture capture;
|
||||
REQUIRE(erased.runtime_read("value", &capture, &capture_int) == Runtime_Access_Result::ok);
|
||||
REQUIRE(capture.value == 10);
|
||||
read_done.release();
|
||||
});
|
||||
REQUIRE(!read_done.try_acquire_for(std::chrono::milliseconds(20)));
|
||||
}
|
||||
REQUIRE(read_done.try_acquire_for(std::chrono::seconds(2)));
|
||||
reader.join();
|
||||
std::binary_semaphore write_done{0};
|
||||
std::jthread writer;
|
||||
{
|
||||
auto guard = device.lock_shared<&Runtime_Device::value>();
|
||||
writer = std::jthread([&] {
|
||||
int value = 30;
|
||||
REQUIRE(erased.runtime_write("value", typeid(int), &value) == Runtime_Access_Result::ok);
|
||||
write_done.release();
|
||||
});
|
||||
REQUIRE(!write_done.try_acquire_for(std::chrono::milliseconds(20)));
|
||||
}
|
||||
REQUIRE(write_done.try_acquire_for(std::chrono::seconds(2)));
|
||||
writer.join();
|
||||
REQUIRE(device.value == 30);
|
||||
}
|
||||
static void test_runtime_read_only_fast_path() {
|
||||
Runtime_Device device;
|
||||
Property_Object_Base& erased = device;
|
||||
REQUIRE(device.lock_slot<&Runtime_Device::serial>() == Resolved_Synchronization_View::unsynchronized_slot);
|
||||
std::binary_semaphore done{0};
|
||||
std::jthread reader;
|
||||
{
|
||||
auto guard = device.lock_unique<&Runtime_Device::value>();
|
||||
reader = std::jthread([&] {
|
||||
Runtime_Read_Capture capture;
|
||||
REQUIRE(erased.runtime_read("serial", &capture, &capture_int) == Runtime_Access_Result::ok);
|
||||
REQUIRE(capture.value == 7);
|
||||
done.release();
|
||||
});
|
||||
REQUIRE(done.try_acquire_for(std::chrono::milliseconds(200)));
|
||||
}
|
||||
reader.join();
|
||||
}
|
||||
int main() {
|
||||
test_runtime_metadata_and_results();
|
||||
test_runtime_access_uses_managed_synchronization();
|
||||
test_runtime_read_only_fast_path();
|
||||
}
|
||||
Reference in New Issue
Block a user