Skip to content

Instantly share code, notes, and snippets.

@ChunMinChang
Last active October 1, 2018 20:51
Show Gist options
  • Save ChunMinChang/1acf672babd4e8f79fcf83fa228d1461 to your computer and use it in GitHub Desktop.
Save ChunMinChang/1acf672babd4e8f79fcf83fa228d1461 to your computer and use it in GitHub Desktop.
Wrap native type by tuple struct
#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);
}
#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);
}
#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
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.
}
}
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
#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;
}
#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;
}
// 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();
}
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