Why does managing OpenGl in Rust require Unsafe Code? - opengl

I've been writing an app in Rust that uses some OpenGL, and I've observed a trend in how OpenGl is accessed/managed in rust code. Frequently it seems that managing or creating an OpenGl context requires unsafe.
Why do these examples require unsafe code? I haven't been running into any problems because of this unsafe designator, but I'm just curious as to why its there. What kind of problems or constraints do these unsafe requirements impose on developers?
from glutins Multi-Window example
//...
let mut windows = std::collections::HashMap::new();
for index in 0..3 {
let title = format!("Charming Window #{}", index + 1);
let wb = WindowBuilder::new().with_title(title);
let windowed_context = ContextBuilder::new().build_windowed(wb, &el).unwrap();
//uses unsafe code
let windowed_context = unsafe { windowed_context.make_current().unwrap() };
let gl = support::load(&windowed_context.context());
let window_id = windowed_context.window().id();
let context_id = ct.insert(ContextCurrentWrapper::PossiblyCurrent(
ContextWrapper::Windowed(windowed_context),
));
windows.insert(window_id, (context_id, gl, index));
}
//...
from fltk-rs glow demo
//...
unsafe {
let gl = glow::Context::from_loader_function(|s| {
win.get_proc_address(s) as *const _
});
let vertex_array = gl
.create_vertex_array()
.expect("Cannot create vertex array");
gl.bind_vertex_array(Some(vertex_array));
let program = gl.create_program().expect("Cannot create program");
//...
win.draw(move |w| {
gl.clear(glow::COLOR_BUFFER_BIT);
gl.draw_arrays(glow::TRIANGLES, 0, 3);
w.swap_buffers();
});
}
//...

OpenGL is implemented as a library with a C ABI. If you want to call a C function from rust, it always means you have to use unsafe because the C implementation knows nothing about the safety features of rust and naturally doesn't support them. Furthermore, OpenGL uses raw pointers in its API to pass data from or to the GL, which also requires unsafe code in rust.

Related

Problems Translating C++ 'extern "C" __declspec(dllexport)' struct to Rust

I am currently attempting to rebuild and update a Project written in Rust (more specifically it's an SKSE64 plugin for Skyrim: https://github.com/lukasaldersley/sse-mod-skyrim-search-se forked from qbx2)
The last problem I'm facing is the library now requires a struct to be exported from our library for version checking.
I have attempted many probably stupid ways to implement this, but I can't get it to work.
The c++ code is as follows:
struct SKSEPluginVersionData
{
enum
{
kVersion = 1,
};
enum
{
// set this if you are using a (potential at this time of writing) post-AE version of the Address Library
kVersionIndependent_AddressLibraryPostAE = 1 << 0,
// set this if you exclusively use signature matching to find your addresses and have NO HARDCODED ADDRESSES
kVersionIndependent_Signatures = 1 << 1,
};
UInt32 dataVersion; // set to kVersion
UInt32 pluginVersion; // version number of your plugin
char name[256]; // null-terminated ASCII plugin name
char author[256]; // null-terminated ASCII plugin author name (can be empty)
char supportEmail[256]; // null-terminated ASCII support email address (can be empty)
// version compatibility
UInt32 versionIndependence; // set to one of the kVersionIndependent_ enums or zero
UInt32 compatibleVersions[16]; // zero-terminated list of RUNTIME_VERSION_ defines your plugin is compatible with
UInt32 seVersionRequired; // minimum version of the script extender required, compared against PACKED_SKSE_VERSION
// you probably should just set this to 0 unless you know what you are doing
};
#define RUNTIME_VERSION_1_6_318 0x010613E0
extern "C" {
__declspec(dllexport) SKSEPluginVersionData SKSEPlugin_Version =
{
SKSEPluginVersionData::kVersion,
1,
"Skyrim Search",
"qbx2",
"",
0, // not version independent
{ RUNTIME_VERSION_1_6_318, 0 }, // RUNTIME_VERSION_1_6_318 is
0, // works with any version of the script extender. you probably do not need to put anything here
};
};
What I've come up with so far in Rust is:
enum KVersionenum {
KVersion=1,
}
#[repr(C)]
pub struct SKSEPluginVersionData {
dataVersion: u32,
pluginVersion: u32,
name: [char;256],
author: [char;256],
supportEmail: [char;256],
versionIndependence: u32,
compatibleVersions: [u32;16],
seVersionRequired: u32,
}
//0x010613E0 is RUNTIME_VERSION_1_6_318
//how can I do this OUTSIDE of a method and how can I make it public to the dll? is that even possible?
let SKSEPlugin_Version = SKSEPluginVersionData {
dataVersion: KVersionenum::KVersion as u32,
pluginVersion: 1,
name: "Skyrim Search\0", //this doesn't work, how can I fill this char array?
author: "qbx2 / lukasaldersley\0", //same here
supportEmail: "something#something.something\0", //and here
versionIndependence: 0,
compatibleVersions: [0x010613E0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], //I'm sure this is a horrible way of doing this
seVersionRequired: 0,
};
When I tried to use the let thingy outside of a function the compiler complained about expecting 'item' but my google-fu isn't good enough to find any useful information there because I just kept finding information about items in the videogame Rust.
For the car array/string problem I have come across that std:ffi stuff and am completely lost in it's documentation but as far as I can tell it will only ever deal with pointers, which is not what I need.
The two questions now are how to I fill these char arrays (I cannot just pass a pointer) and how do I create an instance of this struct as a global variable (or however Rust calls it) that I can export since the let name = something {...} doesn't work.
As far as I can tell exporting to the dll for a function would look like this, but I assume it wouldn't work the same way for that struct.
#[no_mangle]
pub extern "C" fn SKSEPlugin_Query(skse: *const SKSEInterface, info: *mut PluginInfo) -> bool {...}
Is it even possible to do this?
Could someone help me here or at least point me in the right direction?
Please note I am an absolute beginner to Rust and apparently falsely assumed just adding a struct wouldn't be so complicated.
First, a char in Rust is a 32-bit value while its 8-bit in C++ (that's not strictly true but Rust doesn't support architectures where it isn't). So the
name, author, and supportEmail fields should be u8 arrays.
You can export a global value by using #[no_mangle] on a public static variable:
#[no_mangle]
pub static SKSEPlugin_Version: SKSEPluginVersionData = SKSEPluginVersionData {
...
};
See: Can a Rust constant/static be exposed to C?
You can initialize a u8 array from a literal using a byte string and dereferencing it: *b"...". Unfortunately, there's no shorthand like in C++ for zero-padding the undetermined part of the array, so you'd be left with:
name: *b"Skyrim Search\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
author: *b"qbx2 / lukasaldersley\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
supportEmail: *b"something#something.something\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
compatibleVersions: [0x010613E0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
Which honestly, is not nice at all. You can clean this up with some functions that pad the array for you, however, initializing static variables requires const functions, which are still relatively immature in Rust so we can't use things like for loops or traits to help us out:
const fn zero_pad_u8<const N: usize, const M: usize>(arr: &[u8; N]) -> [u8; M] {
let mut m = [0; M];
let mut i = 0;
while i < N {
m[i] = arr[i];
i += 1;
}
m
}
const fn zero_pad_u32<const N: usize, const M: usize>(arr: &[u32; N]) -> [u32; M] {
let mut m = [0; M];
let mut i = 0;
while i < N {
m[i] = arr[i];
i += 1;
}
m
}
...
name: zero_pad_u8(b"Skyrim Search"),
author: zero_pad_u8(b"qbx2 / lukasaldersley"),
supportEmail: zero_pad_u8(b"something#something.something"),
compatibleVersions: zero_pad_u32(&[0x010613E0]),
Still not that nice, but at least its manageable. There may be a crate available that can do this for you.
Lastly, you don't have to use the same field naming convention as used in C++ since its just the order and type that matters, so I'd recommend using snake_case, but if you do want to keep the same names for consistency, you can put the #[allow(non_snake_case)] attribute on SKSEPluginVersionData to suppress the compiler warnings.
I would also recommend making a constant for that magic value instead of just a comment:
const RUNTIME_VERSION_1_6_318: u32 = 0x010613E0;
See the full thing on the playground.

What is the advantage to use OpenGL methods in Rust via a struct rather than accessing them globally?

I am currently porting an OpenGL application written in C++ to Rust and came across a design question that may have other implications since I am pretty new to Rust.
As of now I am using a combination of glutin and gl_generator. Generating global functions in the bindings module yields the following API:
gl::load_with(|symbol| context.get_proc_address(symbol));
// anywhere else since it's global
gl::CreateProgram();
This is pretty much the same paradigm that I used for my wrapper classes in C++ that access global methods:
// ...
FShader::~FShader() {
if (Handle != 0) {
glDeleteProgram(Handle);
}
}
auto FShader::bind() const -> void { glUseProgram(Handle); }
// ...
After reading further posts on the internet I noticed some people suggesting to use the struct generator and work with an objects rather than global methods.
let gl = Rc::new(gl::Gl::load_with(|symbol| context.get_proc_address(symbol)));
// ...
pub struct Shader {
gl: Rc<gl::Gl>,
handle: u32,
}
impl Drop for Shader {
fn drop(&mut self) {
unsafe {
self.gl.DeleteProgram(self.handle);
}
}
}
impl Shader {
pub fn new(gl: Rc<gl::Gl>) -> Result<Shader, ()> {
let handle = unsafe { gl.CreateProgram() };
// ...
Ok(Shader { gl, handle })
}
}
This approach is definitely more verbose since all wrapper classes would have to store a ref to the gl::Gl object to be able to release resources for instance. The signature of Drop::drop seems to be the reason why sharing the reference across objects is necessary. Are there any other advantages for doing this? Is the use of a Rc the best approach or is it more reasonable to work with regular references?

Translating Cpp to Rust, handling a global static object

I'm a beginner translating a familiar Cpp project to Rust. The project contains a class called Globals which stores global config parameters. Here is an extract from its cpp file:
static Globals &__get()
{
static Globals globals;
return globals;
}
const Globals &Globals::get()
{
auto &globals = __get();
if (!globals.initialized) {
throw std::runtime_error("Initialize globals first");
}
return globals;
}
void Globals::set(const Globals &globals)
{
__get() = globals;
}
How would I translate this to Rust? From what I can tell __get() implements some kind of singleton logic. I've read about the lazy_static crate to achieve something similar, but unlocking the variable each time I want to read its value seems to be too verbose. Isn't it possible to achieve this using an interface like Globals::get() in the cpp code.
I rarely post, so if I forgot something, just tell me and I'll provide details. Thanks!
I've read about the lazy_static crate to achieve something similar, but unlocking the variable each time I want to read its value seems to be too verbose.
For a good reason: "safe rust" includes what you'd call thread-safety by design. An unprotected mutable global is wildly unsafe.
Which... is why interacting with a mutable static requires unsafe (whether reading or writing).
The translation from C++ is quite straightforward[0] and can easily be inferred from the reference section on statics, the only real divergence is that a Rust static must be initialised.
Also note that if you do not need mutation then you can lazy_static! a readonly value (you don't need the mutex at all), and don't need to unlock anything.
[0] though much simplified by doing away with the unnecessary __get
Rust requires memory safety in safe code - thus, you cannot have a mutable static in safe code. You CAN have an atomic static (see AtomicBool or AtomicU64 for examples), but for a normal type, you will need some sort of locking mechanism, such as an RwLock or Mutex (if performance is your thing, the parking_lot crate provides more performant implementations than the Rust standard library)
If you don't want to handle locking yourself, may I suggest making a wrapper object using getter/setter methods?
use std::sync::{Arc, RwLock};
use once_cell::sync::Lazy;
static GLOBAL: Lazy<Global> = Lazy::new(Global::new);
struct GlobalThingymajig {
pub number: u32,
pub words: String,
}
pub struct Global(Arc<RwLock<GlobalThingymajig>>);
impl Global {
pub fn new() -> Self {
Self(Arc::new(RwLock::new(
GlobalThingymajig {
number: 42,
words: "The Answer to Life, The Universe, and Everything".into()
}
)))
}
pub fn number(&self) -> u32 {
self.0.read().unwrap().number
}
pub fn words(&self) -> String {
self.0.read().unwrap().words.clone()
}
pub fn set_number(&self, new_number: u32) {
let mut writer = self.0.write().unwrap();
writer.number = new_number;
}
pub fn set_words(&self, new_words: String) {
let mut writer = self.0.write().unwrap();
writer.words = new_words;
}
}
You can see this example on the Rust Playground here

How to wrapping in C a C++ function with vector<struct> output argument? [duplicate]

I'm looking to develop a set of C APIs that will wrap around our existing C++ APIs to access our core logic (written in object-oriented C++). This will essentially be a glue API that allows our C++ logic to be usable by other languages. What are some good tutorials, books, or best-practices that introduce the concepts involved in wrapping C around object-oriented C++?
This is not too hard to do by hand, but will depend on the size of your interface.
The cases where I've done it were to enable use of our C++ library from within pure C code, and thus SWIG was not much help. (Well maybe SWIG can be used to do this, but I'm no SWIG guru and it seemed non-trivial)
All we ended up doing was:
Every object is passed about in C an opaque handle.
Constructors and destructors are wrapped in pure functions
Member functions are pure functions.
Other builtins are mapped to C equivalents where possible.
So a class like this (C++ header)
class MyClass
{
public:
explicit MyClass( std::string & s );
~MyClass();
int doSomething( int j );
}
Would map to a C interface like this (C header):
struct HMyClass; // An opaque type that we'll use as a handle
typedef struct HMyClass HMyClass;
HMyClass * myStruct_create( const char * s );
void myStruct_destroy( HMyClass * v );
int myStruct_doSomething( HMyClass * v, int i );
The implementation of the interface would look like this (C++ source)
#include "MyClass.h"
extern "C"
{
HMyClass * myStruct_create( const char * s )
{
return reinterpret_cast<HMyClass*>( new MyClass( s ) );
}
void myStruct_destroy( HMyClass * v )
{
delete reinterpret_cast<MyClass*>(v);
}
int myStruct_doSomething( HMyClass * v, int i )
{
return reinterpret_cast<MyClass*>(v)->doSomething(i);
}
}
We derive our opaque handle from the original class to avoid needing any casting, and (This didn't seem to work with my current compiler). We have to make the handle a struct as C doesn't support classes.
So that gives us the basic C interface. If you want a more complete example showing one way that you can integrate exception handling, then you can try my code on github : https://gist.github.com/mikeando/5394166
The fun part is now ensuring that you get all the required C++ libraries linked into you larger library correctly. For gcc (or clang) that means just doing the final link stage using g++.
I think Michael Anderson's answer is on the right track but my approach would be different. You have to worry about one extra thing: Exceptions. Exceptions are not part of the C ABI so you cannot let Exceptions ever be thrown past the C++ code. So your header is going to look like this:
#ifdef __cplusplus
extern "C"
{
#endif
void * myStruct_create( const char * s );
void myStruct_destroy( void * v );
int myStruct_doSomething( void * v, int i );
#ifdef __cplusplus
}
#endif
And your wrapper's .cpp file will look like this:
void * myStruct_create( const char * s ) {
MyStruct * ms = NULL;
try { /* The constructor for std::string may throw */
ms = new MyStruct(s);
} catch (...) {}
return static_cast<void*>( ms );
}
void myStruct_destroy( void * v ) {
MyStruct * ms = static_cast<MyStruct*>(v);
delete ms;
}
int myStruct_doSomething( void * v, int i ) {
MyStruct * ms = static_cast<MyStruct*>(v);
int ret_value = -1; /* Assuming that a negative value means error */
try {
ret_value = ms->doSomething(i);
} catch (...) {}
return ret_value;
}
Even better: If you know that all you need as a single instance of MyStruct, don't take the risk of dealing with void pointers being passed to your API. Do something like this instead:
static MyStruct * _ms = NULL;
int myStruct_create( const char * s ) {
int ret_value = -1; /* error */
try { /* The constructor for std::string may throw */
_ms = new MyStruct(s);
ret_value = 0; /* success */
} catch (...) {}
return ret_value;
}
void myStruct_destroy() {
if (_ms != NULL) {
delete _ms;
}
}
int myStruct_doSomething( int i ) {
int ret_value = -1; /* Assuming that a negative value means error */
if (_ms != NULL) {
try {
ret_value = _ms->doSomething(i);
} catch (...) {}
}
return ret_value;
}
This API is a lot safer.
But, as Michael mentioned, linking may get pretty tricky.
Hope this helps
It is not hard to expose C++ code to C, just use the Facade design pattern
I am assuming your C++ code is built into a library, all you need to do is make one C module in your C++ library as a Facade to your library along with a pure C header file. The C module will call the relevant C++ functions
Once you do that your C applications and library will have full access to the C api you exposed.
for example, here is a sample Facade module
#include <libInterface.h>
#include <objectedOrientedCppStuff.h>
int doObjectOrientedStuff(int *arg1, int arg2, char *arg3) {
Object obj = ObjectFactory->makeCppObj(arg3); // doing object oriented stuff here
obj->doStuff(arg2);
return obj->doMoreStuff(arg1);
}
you then expose this C function as your API and you can use it freely as a C lib with out worrying about
// file name "libIntrface.h"
extern int doObjectOrientedStuff(int *, int, char*);
Obviously this is a contrived example but this is the easiest way to expos a C++ library to C
I would think you may be able to get some ideas on direction and/or possibly utilize directly SWIG. I would think that going over a few of the examples would at least give you an idea of what kinds of things to consider when wrapping one API into another. The exercise could be beneficial.
SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages. SWIG is used with different types of languages including common scripting languages such as Perl, PHP, Python, Tcl and Ruby. The list of supported languages also includes non-scripting languages such as C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Lua, Modula-3, OCAML, Octave and R. Also several interpreted and compiled Scheme implementations (Guile, MzScheme, Chicken) are supported. SWIG is most commonly used to create high-level interpreted or compiled programming environments, user interfaces, and as a tool for testing and prototyping C/C++ software. SWIG can also export its parse tree in the form of XML and Lisp s-expressions. SWIG may be freely used, distributed, and modified for commercial and non-commercial use.
Just replace the concept of an object with a void * (often referred to as an opaque type in C oriented libraries) and reuse everything you know from C++.
I think using SWIG is the best answer... not only it avoid reinventing wheel but it is reliable and also promote a continuity in development rather than one shooting the problem.
High frequency problems need to be addressed by a long term solution.

Developing C wrapper API for Object-Oriented C++ code

I'm looking to develop a set of C APIs that will wrap around our existing C++ APIs to access our core logic (written in object-oriented C++). This will essentially be a glue API that allows our C++ logic to be usable by other languages. What are some good tutorials, books, or best-practices that introduce the concepts involved in wrapping C around object-oriented C++?
This is not too hard to do by hand, but will depend on the size of your interface.
The cases where I've done it were to enable use of our C++ library from within pure C code, and thus SWIG was not much help. (Well maybe SWIG can be used to do this, but I'm no SWIG guru and it seemed non-trivial)
All we ended up doing was:
Every object is passed about in C an opaque handle.
Constructors and destructors are wrapped in pure functions
Member functions are pure functions.
Other builtins are mapped to C equivalents where possible.
So a class like this (C++ header)
class MyClass
{
public:
explicit MyClass( std::string & s );
~MyClass();
int doSomething( int j );
}
Would map to a C interface like this (C header):
struct HMyClass; // An opaque type that we'll use as a handle
typedef struct HMyClass HMyClass;
HMyClass * myStruct_create( const char * s );
void myStruct_destroy( HMyClass * v );
int myStruct_doSomething( HMyClass * v, int i );
The implementation of the interface would look like this (C++ source)
#include "MyClass.h"
extern "C"
{
HMyClass * myStruct_create( const char * s )
{
return reinterpret_cast<HMyClass*>( new MyClass( s ) );
}
void myStruct_destroy( HMyClass * v )
{
delete reinterpret_cast<MyClass*>(v);
}
int myStruct_doSomething( HMyClass * v, int i )
{
return reinterpret_cast<MyClass*>(v)->doSomething(i);
}
}
We derive our opaque handle from the original class to avoid needing any casting, and (This didn't seem to work with my current compiler). We have to make the handle a struct as C doesn't support classes.
So that gives us the basic C interface. If you want a more complete example showing one way that you can integrate exception handling, then you can try my code on github : https://gist.github.com/mikeando/5394166
The fun part is now ensuring that you get all the required C++ libraries linked into you larger library correctly. For gcc (or clang) that means just doing the final link stage using g++.
I think Michael Anderson's answer is on the right track but my approach would be different. You have to worry about one extra thing: Exceptions. Exceptions are not part of the C ABI so you cannot let Exceptions ever be thrown past the C++ code. So your header is going to look like this:
#ifdef __cplusplus
extern "C"
{
#endif
void * myStruct_create( const char * s );
void myStruct_destroy( void * v );
int myStruct_doSomething( void * v, int i );
#ifdef __cplusplus
}
#endif
And your wrapper's .cpp file will look like this:
void * myStruct_create( const char * s ) {
MyStruct * ms = NULL;
try { /* The constructor for std::string may throw */
ms = new MyStruct(s);
} catch (...) {}
return static_cast<void*>( ms );
}
void myStruct_destroy( void * v ) {
MyStruct * ms = static_cast<MyStruct*>(v);
delete ms;
}
int myStruct_doSomething( void * v, int i ) {
MyStruct * ms = static_cast<MyStruct*>(v);
int ret_value = -1; /* Assuming that a negative value means error */
try {
ret_value = ms->doSomething(i);
} catch (...) {}
return ret_value;
}
Even better: If you know that all you need as a single instance of MyStruct, don't take the risk of dealing with void pointers being passed to your API. Do something like this instead:
static MyStruct * _ms = NULL;
int myStruct_create( const char * s ) {
int ret_value = -1; /* error */
try { /* The constructor for std::string may throw */
_ms = new MyStruct(s);
ret_value = 0; /* success */
} catch (...) {}
return ret_value;
}
void myStruct_destroy() {
if (_ms != NULL) {
delete _ms;
}
}
int myStruct_doSomething( int i ) {
int ret_value = -1; /* Assuming that a negative value means error */
if (_ms != NULL) {
try {
ret_value = _ms->doSomething(i);
} catch (...) {}
}
return ret_value;
}
This API is a lot safer.
But, as Michael mentioned, linking may get pretty tricky.
Hope this helps
It is not hard to expose C++ code to C, just use the Facade design pattern
I am assuming your C++ code is built into a library, all you need to do is make one C module in your C++ library as a Facade to your library along with a pure C header file. The C module will call the relevant C++ functions
Once you do that your C applications and library will have full access to the C api you exposed.
for example, here is a sample Facade module
#include <libInterface.h>
#include <objectedOrientedCppStuff.h>
int doObjectOrientedStuff(int *arg1, int arg2, char *arg3) {
Object obj = ObjectFactory->makeCppObj(arg3); // doing object oriented stuff here
obj->doStuff(arg2);
return obj->doMoreStuff(arg1);
}
you then expose this C function as your API and you can use it freely as a C lib with out worrying about
// file name "libIntrface.h"
extern int doObjectOrientedStuff(int *, int, char*);
Obviously this is a contrived example but this is the easiest way to expos a C++ library to C
I would think you may be able to get some ideas on direction and/or possibly utilize directly SWIG. I would think that going over a few of the examples would at least give you an idea of what kinds of things to consider when wrapping one API into another. The exercise could be beneficial.
SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages. SWIG is used with different types of languages including common scripting languages such as Perl, PHP, Python, Tcl and Ruby. The list of supported languages also includes non-scripting languages such as C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Lua, Modula-3, OCAML, Octave and R. Also several interpreted and compiled Scheme implementations (Guile, MzScheme, Chicken) are supported. SWIG is most commonly used to create high-level interpreted or compiled programming environments, user interfaces, and as a tool for testing and prototyping C/C++ software. SWIG can also export its parse tree in the form of XML and Lisp s-expressions. SWIG may be freely used, distributed, and modified for commercial and non-commercial use.
Just replace the concept of an object with a void * (often referred to as an opaque type in C oriented libraries) and reuse everything you know from C++.
I think using SWIG is the best answer... not only it avoid reinventing wheel but it is reliable and also promote a continuity in development rather than one shooting the problem.
High frequency problems need to be addressed by a long term solution.