Last active
October 1, 2018 20:51
-
-
Save ChunMinChang/1acf672babd4e8f79fcf83fa228d1461 to your computer and use it in GitHub Desktop.
Wrap native type by tuple struct
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "device.h" | |
#include <assert.h> // assert | |
#include <string.h> // memcpy | |
// Private APIs | |
// ============================================================================ | |
const int MAX_TYPE_NUM = VERSION + 1; | |
typedef struct { | |
DeviceId id; | |
DeviceVersion version; | |
} Device; | |
Device* get_or_create_device() { | |
static Device instance = { 123, 4.5 }; // default setting. | |
return &instance; | |
} | |
size_t get_device_data_size(DataType type) { | |
assert(type < MAX_TYPE_NUM); | |
static size_t sizes[MAX_TYPE_NUM] = { | |
sizeof(DeviceId), // ID | |
sizeof(DeviceVersion) // VERSION | |
}; | |
return sizes[type]; | |
} | |
void* get_device_data_source(DataType type) { | |
assert(type < MAX_TYPE_NUM); | |
Device* device = get_or_create_device(); | |
void* sources[MAX_TYPE_NUM] = { | |
&device->id, | |
&device->version | |
}; | |
return sources[type]; | |
// Notice that the following code doesn't work: | |
// --------------------------------------------- | |
// if (type == ID) { | |
// DeviceId id = device->id; | |
// return (void*) &id; | |
// } else { // type == VERSION | |
// DeviceVersion version = device->version; | |
// return (void*) &version; | |
// } | |
// --------------------------------------------- | |
// `Device{Id, Version} x = device->{id, version}` means that we copy the | |
// value of `device->*` to a local variable `x`. This local variable `x` | |
// will be destroyed after finish this function call. Thus, if we return its | |
// address, this address will point to a garbage memory that was used to | |
// store the `x`, which is destroyed after return. | |
} | |
typedef enum { | |
GET, | |
SET | |
} Action; | |
Status set_or_get_device_data(Action action, | |
DataType type, | |
size_t size, | |
void* data) { | |
if (type > MAX_TYPE_NUM) { | |
return BAD_TYPE; | |
} | |
if (!data) { | |
return BAD_POINTER; | |
} | |
size_t device_data_size = get_device_data_size(type); | |
if (device_data_size <= 0 || | |
size < device_data_size) { | |
return BAD_SIZE; | |
} | |
void* device_data = get_device_data_source(type); | |
assert(device_data); | |
void* source = NULL; | |
void* dest = NULL; | |
if (action == GET) { | |
source = device_data; | |
dest = data; | |
} else { | |
source = data; | |
dest = device_data; | |
} | |
memcpy(dest, source, device_data_size); | |
return OK; | |
} | |
// Public APIs | |
// ============================================================================ | |
Status get_device_data(DataType type, size_t size, void* data) { | |
return set_or_get_device_data(GET, type, size, data); | |
} | |
Status set_device_data(DataType type, size_t size, const void* data) { | |
return set_or_get_device_data(SET, type, size, (void*) data); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "device.h" | |
#include <cassert> // assert | |
#include <cstring> // memcpy | |
// Private APIs | |
// ============================================================================ | |
const int MAX_TYPE_NUM = VERSION + 1; | |
class Device final | |
{ | |
public: | |
static Device& get_instance() { | |
static Device instance(123, 4.5); | |
return instance; | |
} | |
static size_t data_size(DataType type) { | |
assert(type < MAX_TYPE_NUM); | |
static size_t sizes[MAX_TYPE_NUM] = { | |
sizeof(DeviceId), // ID | |
sizeof(DeviceVersion) // VERSION | |
}; | |
return sizes[type]; | |
} | |
static void* get_data_source(DataType type) { | |
assert(type < MAX_TYPE_NUM); | |
Device& device = get_instance(); | |
void* sources[MAX_TYPE_NUM] = { | |
&device._id, | |
&device._version | |
}; | |
return sources[type]; | |
// Notice that the following code doesn't work: | |
// --------------------------------------------- | |
// if (type == ID) { | |
// DeviceId id = device._id; | |
// return (void*) &id; | |
// } else { // type == VERSION | |
// DeviceVersion version = device._version; | |
// return (void*) &version; | |
// } | |
// --------------------------------------------- | |
// `Device{Id, Version} x = device.{_id, _version}` means that we copy the | |
// value of `device._*` to a local variable `x`. This local variable `x` | |
// will be destroyed after finish this function call. Thus, if we return | |
// its address, this address will point to a garbage memory that was used | |
// to store the `x`, which is destroyed after return. | |
} | |
private: | |
Device(DeviceId id, DeviceVersion version) | |
: _id(id) | |
, _version(version) | |
{ | |
} | |
Device(Device const&) = delete; // Disable copy constructor. | |
void operator=(Device const&) = delete; // Disable assignment. | |
~Device() {} | |
DeviceId _id; | |
DeviceVersion _version; | |
}; | |
typedef enum { | |
GET, | |
SET | |
} Action; | |
Status set_or_get_device_data(Action action, | |
DataType type, | |
size_t size, | |
void* data) { | |
if (type > MAX_TYPE_NUM) { | |
return BAD_TYPE; | |
} | |
if (!data) { | |
return BAD_POINTER; | |
} | |
size_t device_data_size = Device::data_size(type); | |
if (device_data_size <= 0 || | |
size < device_data_size) { | |
return BAD_SIZE; | |
} | |
void* device_data = Device::get_data_source(type); | |
assert(device_data); | |
void* source = NULL; | |
void* dest = NULL; | |
if (action == GET) { | |
source = device_data; | |
dest = data; | |
} else { | |
source = data; | |
dest = device_data; | |
} | |
memcpy(dest, source, device_data_size); | |
return OK; | |
} | |
// Public APIs | |
// ============================================================================ | |
Status get_device_data(DataType type, size_t size, void* data) { | |
return set_or_get_device_data(GET, type, size, data); | |
} | |
Status set_device_data(DataType type, size_t size, const void* data) { | |
return set_or_get_device_data(SET, type, size, (void*) data); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef EXT_H | |
#define EXT_H | |
#include <stdint.h> // uint32_t | |
#include <stdlib.h> // size_t | |
typedef enum { | |
OK = 0, | |
BAD_POINTER = 1, | |
BAD_SIZE = 2, | |
BAD_TYPE = 3 | |
} Status; | |
typedef enum { | |
ID, | |
VERSION | |
} DataType; | |
typedef uint32_t DeviceId; | |
typedef float DeviceVersion; | |
#ifdef __cplusplus | |
extern "C" | |
{ | |
#endif | |
Status get_device_data(DataType type, size_t size, void* data); | |
Status set_device_data(DataType type, size_t size, const void* data); | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif // EXT_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use super::sys; | |
use std::mem; // For mem::{uninitialized(), size_of()} | |
use std::os::raw::c_void; | |
#[derive(Debug)] | |
pub enum Error { | |
BadPointer, | |
BadSize, | |
BadType, | |
} | |
// To convert a sys::Status to a Error. | |
impl From<sys::Status> for Error { | |
fn from(status: sys::Status) -> Error { | |
match status { | |
sys::BAD_POINTER => Error::BadPointer, | |
sys::BAD_SIZE => Error::BadSize, | |
sys::BAD_TYPE => Error::BadType, | |
s => panic!("Unknown status: {}", s), | |
} | |
} | |
} | |
pub enum DataType { | |
Id, | |
Version, | |
} | |
// To convert a DataType to a sys::DataType. | |
impl From<DataType> for sys::DataType { | |
fn from(data_type: DataType) -> sys::DataType { | |
match data_type { | |
DataType::Id => sys::ID, | |
DataType::Version => sys::VERSION, | |
} | |
} | |
} | |
// Public APIs: | |
pub fn get_data<T>(data_type: DataType) -> Result<T, Error> { | |
let mut data: T = unsafe { mem::uninitialized() }; | |
let size = mem::size_of::<T>(); | |
let status = unsafe { | |
sys::get_device_data( | |
data_type.into(), // Convert DataType into sys::DataType. | |
size, | |
&mut data as *mut T as *mut c_void, | |
) | |
}; | |
convert_to_result(status)?; | |
Ok(data) | |
} | |
pub fn set_data<T>(data_type: DataType, data: &T) -> Result<(), Error> { | |
let size = mem::size_of::<T>(); | |
let status = unsafe { | |
sys::set_device_data( | |
data_type.into(), // Convert DataType into sys::DataType. | |
size, | |
data as *const T as *const c_void, | |
) | |
}; | |
convert_to_result(status) | |
} | |
// Private APIs: | |
fn convert_to_result(status: sys::Status) -> Result<(), Error> { | |
match status { | |
sys::OK => Ok(()), | |
e => Err(e.into()), // Convert sys::Status into Error. | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all: | |
# Sample in C: | |
# gcc -shared -fPIC device.c -o libdevice.so | |
# gcc sample.c libdevice.so -o sample-c | |
# ./sample-c | |
# Sample in C++: | |
g++ -std=c++11 -shared -fPIC device.cpp -o libdevice.so | |
g++ -std=c++11 sample.cpp libdevice.so -o sample-cpp | |
./sample-cpp | |
# Sample in Rust: | |
rustc sample.rs -L. | |
LD_LIBRARY_PATH=. ./sample | |
clean: | |
# rm sample-c | |
rm sample-cpp | |
rm sample | |
rm libdevice.so |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "device.h" | |
#include <assert.h> // assert | |
#include <stdio.h> // printf | |
Status get_id(DeviceId* id) { | |
return get_device_data(ID, sizeof(*id), id); | |
} | |
Status set_id(DeviceId* id) { | |
return set_device_data(ID, sizeof(*id), id); | |
} | |
Status get_version(DeviceVersion* version) { | |
return get_device_data(VERSION, sizeof(*version), version); | |
} | |
Status set_version(DeviceVersion* version) { | |
return set_device_data(VERSION, sizeof(*version), version); | |
} | |
// Avoid to name this struct to Device, in case compiler confuse this struct | |
// with the Device struct in device.c. | |
typedef struct { | |
DeviceId id; | |
DeviceVersion version; | |
} DeviceData; | |
void get_device(DeviceData* device) { | |
Status s = OK; | |
s = get_id(&device->id); | |
assert(s == OK); | |
s = get_version(&device->version); | |
assert(s == OK); | |
} | |
void set_device(DeviceData* device) { | |
Status s = OK; | |
s = set_id(&device->id); | |
assert(s == OK); | |
s = set_version(&device->version); | |
assert(s == OK); | |
} | |
void show_device(DeviceData* device) { | |
printf("id: %d, version: %f\n", device->id, device->version); | |
} | |
int main() { | |
DeviceData device; | |
get_device(&device); | |
show_device(&device); | |
device.id = 607; | |
device.version = 8.9; | |
set_device(&device); | |
get_device(&device); | |
show_device(&device); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "device.h" | |
#include <cassert> // assert | |
#include <iostream> // std::{cout, endl} | |
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); | |
} | |
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); | |
} | |
}; | |
// 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 = 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 show() { | |
std::cout << "id: " << _id | |
<< ", version: " << _version | |
<< std::endl; | |
} | |
private: | |
DeviceData(DeviceId id, | |
DeviceVersion version) | |
: _id(id) | |
, _version(version) | |
{} | |
~DeviceData() {} | |
static DeviceData get_device() { | |
DeviceId id = 0; | |
DeviceVersion version = 0.0; | |
Status s = OK; | |
s = Utils::get_id(id); | |
assert(s == OK); | |
s = Utils::get_version(version); | |
assert(s == OK); | |
return DeviceData(id, version); | |
} | |
DeviceId _id; | |
DeviceVersion _version; | |
}; | |
int main() { | |
DeviceData& device = DeviceData::get_instance(); | |
device.show(); | |
device.set_id(607); | |
device.set_version(8.9); | |
device.show(); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Contains all the native types and APIs in external library. | |
mod sys; | |
// An adapter layer for the external library. | |
mod ext; | |
mod utils { | |
use super::{ext, sys}; | |
#[derive(Debug)] | |
pub enum Error { | |
Ext(ext::Error), | |
} | |
// To convert a network::Error to a Error. | |
impl From<ext::Error> for Error { | |
fn from(e: ext::Error) -> Error { | |
Error::Ext(e) | |
} | |
} | |
pub struct Device { | |
id: sys::DeviceId, | |
version: sys::DeviceVersion, | |
} | |
impl Device { | |
pub fn new() -> Result<Self, Error> { | |
let id = get_id()?; | |
let version = get_version()?; | |
Ok(Device { | |
id, | |
version, | |
}) | |
} | |
pub fn set_id(&mut self, id: sys::DeviceId) -> Result<(), Error> { | |
self.id = id; | |
set_id(self.id) | |
} | |
pub fn set_version(&mut self, version: sys::DeviceVersion) -> Result<(), Error> { | |
self.version = version; | |
set_version(self.version) | |
} | |
pub fn show(&self) { | |
println!("id: {}, version: {}", self.id, self.version); | |
} | |
} | |
fn get_id() -> Result<sys::DeviceId, Error> { | |
ext::get_data::<sys::DeviceId>(ext::DataType::Id) | |
.map_err(|e| e.into()) | |
} | |
fn set_id(id: sys::DeviceId) -> Result<(), Error> { | |
ext::set_data(ext::DataType::Id, &id) | |
.map_err(|e| e.into()) | |
} | |
fn get_version() -> Result<sys::DeviceVersion, Error> { | |
ext::get_data::<sys::DeviceVersion>(ext::DataType::Version) | |
.map_err(|e| e.into()) | |
} | |
fn set_version(version: sys::DeviceVersion) -> Result<(), Error> { | |
ext::set_data(ext::DataType::Version, &version) | |
.map_err(|e| e.into()) | |
} | |
} | |
fn main() { | |
let mut device = utils::Device::new().unwrap(); | |
device.show(); | |
device.set_id(607).unwrap(); | |
device.set_version(8.9).unwrap(); | |
device.show(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::os::raw::c_void; | |
pub type Status = i32; | |
pub const OK: Status = 0; | |
pub const BAD_POINTER: Status = 1; | |
pub const BAD_SIZE: Status = 2; | |
pub const BAD_TYPE: Status = 3; | |
pub type DataType = i32; | |
pub const ID: DataType = 0; | |
pub const VERSION: DataType = 1; | |
pub type DeviceId = u32; | |
pub type DeviceVersion = f32; | |
#[link(name = "device")] | |
extern "C" { | |
pub fn get_device_data(data_type: DataType, size: usize, data: *mut c_void) -> Status; | |
pub fn set_device_data(data_type: DataType, size: usize, data: *const c_void) -> Status; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment