|
#include "device.h" |
|
#include <cassert> // assert |
|
#include <cstdlib> // free |
|
#include <iostream> // std::{cout, endl} |
|
#include <utility> // std::move |
|
|
|
class Utils final |
|
{ |
|
public: |
|
static Status get_id(DeviceId& id) { |
|
return get_data(ID, id); |
|
} |
|
|
|
static Status set_id(DeviceId& id) { |
|
return set_data(ID, id); |
|
} |
|
|
|
static Status get_version(DeviceVersion& version) { |
|
return get_data(VERSION, version); |
|
} |
|
|
|
static Status set_version(DeviceVersion& version) { |
|
return set_data(VERSION, version); |
|
} |
|
|
|
static Status get_name(DeviceString& name) { |
|
return get_data(NAME, name); |
|
} |
|
|
|
static Status set_name(DeviceString& name) { |
|
return set_data(NAME, name); |
|
} |
|
|
|
static Status get_manufacturer(DeviceString& manufacturer) { |
|
return get_data(MANUFACTURER, manufacturer); |
|
} |
|
|
|
static Status set_manufacturer(DeviceString& manufacturer) { |
|
return set_data(MANUFACTURER, manufacturer); |
|
} |
|
|
|
private: |
|
template<typename T> |
|
static Status get_data(DataType type, T& data) { |
|
return get_device_data(type, sizeof(T), &data); |
|
} |
|
|
|
template<typename T> |
|
static Status set_data(DataType type, T& data) { |
|
return set_device_data(type, sizeof(T), &data); |
|
} |
|
}; |
|
|
|
const char* get_raw_string(DeviceString str) { |
|
if (!str) { |
|
return NULL; |
|
} |
|
|
|
size_t size = 0; |
|
get_string_bytes(str, &size, NULL); |
|
|
|
uint8_t* buffer = (uint8_t*) calloc(1, size); |
|
get_string_bytes(str, &size, buffer); |
|
return (const char*) buffer; |
|
} |
|
|
|
void destroy_raw_string(const char* str) { |
|
free((void*) str); // It's ok to free a NULL pointer. |
|
} |
|
|
|
// A string Wrapper around DeviceString. |
|
// Avoid to name this class to String, in case compiler confuse this class |
|
// with the String class in device_w_string.cpp. |
|
class DevString final |
|
{ |
|
public: |
|
DevString(DeviceString s = nullptr) |
|
: _string(s ? new_string_by_clone(s) : nullptr) |
|
{ |
|
} |
|
|
|
DevString(const char* s) { |
|
assert(s); |
|
_string = new_string_by_clone_raw(s); |
|
} |
|
|
|
// Disable copy constructor. |
|
DevString(const DevString& other) = delete; |
|
// Copy constructor: Do deep copy for inner data. |
|
// DevString(const DevString& other) |
|
// : _string(other.clone_device_string()) |
|
// { |
|
// } |
|
|
|
// Move constructor: Transfer ownership of the inner data. |
|
DevString(DevString&& other) |
|
: _string(other._string) |
|
{ |
|
other._string = nullptr; |
|
} |
|
|
|
~DevString() { |
|
reset(); |
|
} |
|
|
|
DeviceString& get_device_string() { |
|
assert(_string); |
|
return _string; |
|
} |
|
|
|
void reset() { |
|
release_string(_string); |
|
_string = nullptr; |
|
} |
|
|
|
// Disable copy assignmenet. |
|
DevString& operator=(const DevString& other) = delete; |
|
// Copy assignmenet: |
|
// DevString& operator=(const DevString& other) { |
|
// if (&other == this) { |
|
// return *this; |
|
// } |
|
|
|
// reset(); |
|
// _string = other.clone_device_string(); |
|
// return *this; |
|
// } |
|
|
|
// Move assignment: |
|
DevString& operator=(DevString&& other) { |
|
if (&other == this) { |
|
return *this; |
|
} |
|
|
|
reset(); |
|
_string = other._string; |
|
other._string = nullptr; |
|
return *this; |
|
} |
|
|
|
friend std::ostream& operator << (std::ostream &out, const DevString &str) { |
|
if (str._string) { |
|
const char* s = get_raw_string(str._string); |
|
// stream buffer will copy the data. |
|
out << s; |
|
destroy_raw_string(s); |
|
} else { |
|
out << "(empty)"; |
|
} |
|
return out; |
|
} |
|
|
|
private: |
|
DeviceString clone_device_string() const { |
|
return _string ? new_string_by_clone(_string) : nullptr; |
|
} |
|
|
|
DeviceString _string; |
|
}; |
|
|
|
// Avoid to name this class to Device, in case compiler confuse this class |
|
// with the Device class in device.cpp. |
|
class DeviceData final |
|
{ |
|
public: |
|
static DeviceData& get_instance() { |
|
static DeviceData instance = std::move(get_device()); |
|
return instance; |
|
} |
|
|
|
void set_id(DeviceId id) { |
|
_id = id; |
|
Status s = Utils::set_id(_id); |
|
assert(s == OK); |
|
} |
|
|
|
void set_version(DeviceVersion version) { |
|
_version = version; |
|
Status s = Utils::set_version(_version); |
|
assert(s == OK); |
|
} |
|
|
|
void set_name(const char* name) { |
|
assert(name); |
|
_name = DevString(name); |
|
Status s = Utils::set_name(_name.get_device_string()); |
|
assert(s == OK); |
|
} |
|
|
|
void set_manufacturer(const char* manufacturer) { |
|
assert(manufacturer); |
|
_manufacturer = DevString(manufacturer); |
|
Status s = Utils::set_manufacturer(_manufacturer.get_device_string()); |
|
assert(s == OK); |
|
} |
|
|
|
void show() { |
|
std::cout << "id: " << _id |
|
<< ", version: " << _version |
|
<< ", name: " << _name |
|
<< ", manufacturer: " << _manufacturer |
|
<< std::endl; |
|
} |
|
|
|
private: |
|
DeviceData(DeviceId id, |
|
DeviceVersion version, |
|
DeviceString& name, |
|
DeviceString& manufacturer) |
|
: _id(id) |
|
, _version(version) |
|
, _name(name) |
|
, _manufacturer(manufacturer) |
|
{ |
|
// DevString constructor will clone the DeviceString to its inner data, |
|
// so we need to release them by ourselves. |
|
reset_string(name); |
|
reset_string(manufacturer); |
|
} |
|
|
|
// Move constructor: Transfer ownership of the inner data. |
|
DeviceData(DeviceData&& other) |
|
: _id(other._id) |
|
, _version(other._version) |
|
, _name(std::move(other._name)) |
|
, _manufacturer(std::move(other._manufacturer)) |
|
{ |
|
} |
|
|
|
// Disable copy constructor. |
|
DeviceData(const DeviceData& other) = delete; |
|
|
|
~DeviceData() {} |
|
|
|
static DeviceData get_device() { |
|
DeviceId id = 0; |
|
DeviceVersion version = 0.0; |
|
// Set name and manufacturer to nullptr in case we free a random memory |
|
// when calling replace_string. |
|
DeviceString name = nullptr; |
|
DeviceString manufacturer = nullptr; |
|
refresh_data(id, version, name, manufacturer); |
|
return DeviceData(id, version, name, manufacturer); |
|
} |
|
|
|
static void refresh_data(DeviceId& id, |
|
DeviceVersion& version, |
|
DeviceString& name, |
|
DeviceString& manufacturer) { |
|
Status s = OK; |
|
|
|
s = Utils::get_id(id); |
|
assert(s == OK); |
|
|
|
s = Utils::get_version(version); |
|
assert(s == OK); |
|
|
|
s = replace_string_by_func(name, Utils::get_name); |
|
assert(s == OK || s == NO_DATA); // Get `NO_DATA` when string is empty. |
|
|
|
s = replace_string_by_func(manufacturer, Utils::get_manufacturer); |
|
assert(s == OK || s == NO_DATA); // Get `NO_DATA` when string is empty. |
|
} |
|
|
|
typedef Status (*StringFunc)(DeviceString& str); |
|
static Status replace_string_by_func(DeviceString& str, StringFunc func) { |
|
reset_string(str); |
|
return func(str); |
|
} |
|
|
|
static void reset_string(DeviceString& str) { |
|
if (str) { |
|
release_string(str); |
|
} |
|
str = nullptr; |
|
} |
|
|
|
DeviceId _id; |
|
DeviceVersion _version; |
|
DevString _name; |
|
DevString _manufacturer; |
|
}; |
|
|
|
int main() { |
|
DeviceData& device = DeviceData::get_instance(); |
|
device.show(); |
|
|
|
device.set_id(607); |
|
device.set_version(8.9); |
|
device.set_name("Robot 🤖"); |
|
device.set_manufacturer("🎃 jack o'lantern"); |
|
device.show(); |
|
|
|
return 0; |
|
} |