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

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.

Related

Import C++ function returning pointer of structs into VB.NET

I have created a simple DLL in C++, for the sole purpose of isolating the issue and testing on a small scale the importing of a function that returns a list of structs with members of different types.
dll_header.h
#ifdef MY_DLL_EXPORTS
#define MY_DLL_API __declspec(dllexport)
#else
#define MY_DLL_API __declspec(dllimport)
#endif
enum color_type
{
RGB = 1,
MONO = 2
};
struct my_struct
{
unsigned char *name;
char *value;
color_type type;
my_struct* next;
};
extern "C" struct my_struct MY_DLL_API * get_list(void);
dll_header.cpp
#include "dll_header.h"
MY_DLL_API my_struct * get_list(void)
{
my_struct my_list[2];
unsigned char name1[] = "name1";
unsigned char name2[] = "name2";
char val1[] = "val1";
char val2[] = "val2";
my_list[0].name = name1;
my_list[0].value = val1;
my_list[0].type = RGB;
my_list[0].next = &my_list[1];
my_list[0].name = name2;
my_list[0].value = val2;
my_list[0].type = MONO;
my_list[0].next = NULL;
return my_list;
}
Like I said, I have to use these data types (cannot change them to strings or anything else because I am testing something and I need them like this)
Now, in my VB.NET application I import and try to retrieve the list like this from the DLL
Form1.vb
Imports System.Runtime.InteropServices
Imports System.Text
Public Class Form1
Public Enum color_type
RGB = 1
COLOR = 2
End Enum
Private Structure my_struct
Public name As Byte()
Public value As Char()
Public type As color_type
Public next As IntPtr
End Structure
Private Declare Function get_list Lib "my_lib.dll" () As IntPtr
Private my_list As List(Of my_struct)
Private Sub get_list()
Dim my_list_pointer As IntPtr = get_list()
my_list = New List(Of my_struct)
Dim my_item As my_struct
While my_list_pointer <> IntPtr.Zero
my_item = CType(Marshal.PtrToStructure(my_list_pointer, GetType(my_struct)), my_struct)
my_list.Add(my_item)
my_list_pointer = my_item.next
End While
End Sub
I have tried many other methods, specially changing data types but so far here I am, stuck with this exception when trying to run the code:
System.AccessViolationException: 'Attempted to read or write protected
memory. This is often an indication that other memory is corrupt.'
Trying to find a way to make these two understand each other
Either use mixed mode C++ to do the translation between native and managed types or use types that are compatible with P/Invoke.
Looking how Win32 define structure (and function that use those) is a good start to help you understand how to define compatible structures as one of the main intent of P/Invoke is to be able to use Win32 API from managed code.
If you go for mixed mode, then you can do whatever required translation between both world as you can mix native C++ and C++/CLI in the same assembly. So essentially, you would write code that convert the native structure into a managed one and then VB.NET will be able to use that managed code (assuming that you use types available in VB.NET for example).
Well, if you go for the mixed mode route, you generally ends up with 3 DLLs/Assemblies/Executable as you would have the original C++ DLL (native code), the VB.NET code (managed) and the mixed mode C++ assembly in between.
Update
While it could be relatively easy to provide code if the list is read only, if this is not the case, it could be much harder depending how the code is expected to be used and what are the availaible function in the existing DLL.
The starting point would be to create a ref class in C++/CLI.
Some similar questions
https://bytes.com/topic/c-sharp/answers/674468-passing-linked-list-c-dll-c-pinvoke
Return list of points (x,y,z) from C to C# using PInvoke
PInvoke of self referential struct from C++
Problem with your code
Your exported function retuns a pointer to a local variable which is undefined behavior.
Additional observation
If your original list is an array, the why making it a list also? An array would be easier to marshal and use. And probably perform better too.
When defining you structure, you have be explicit on how strings are marshalled and the alignment that should be used. You shoull validate that everything is as expected.

Is it possible to export/wrap a complex Go struct to C?

I own a Go library, gofileseq, for which I would like to try and made a C/C++ binding.
It is pretty straightforward to be able to export functions that use simple types (ints, strings, ...). It is even easy enough to export data from custom Go types to C by defining a C struct and translating the Go type to it, to be used in the exported functions, since you are allocating C memory to do it. But with the go 1.5 cgo rules I am finding it difficult to figure out how to export functionality from a more complex struct that stores state.
Example of a struct from gofileseq that I would like to export somehow to a C++ binding:
// package fileseq
//
type FrameSet struct {
frange string
rangePtr *ranges.InclusiveRanges
}
func NewFrameSet(frange string) (*FrameSet, error) {
// bunch of processing to set up internal state
}
func (s *FrameSet) Len() int {
return s.rangePtr.Len()
}
// package ranges
//
type InclusiveRanges struct {
blocks []*InclusiveRange
}
type InclusiveRange struct {
start int
end int
step int
cachedEnd int
isEndCached bool
cachedLen int
isLenCached bool
}
As you can see, the FrameSet type that I want to expose contains a slice of pointers to an underlying type, each of which stores state.
Ideally, I would love to be able to store a void* on a C++ class, and make it just a simple proxy for calling back into exported Go functions with the void*. But the cgo rules disallow C storing a Go pointer longer than the function call. And I am failing to see how I could use an approach of defining C++ classes that could be allocated and used to operate with my Go library.
Is it possible to wrap complex types for exposure to C/C++?
Is there a pattern that would allow a C++ client to create a Go FrameSet?
Edit
One idea I can think of would be to let C++ create objects in Go that get stored on the Go side in a static map[int]*FrameSet and then return the int id to C++. Then all the C++ operations make requests into Go with the id. Does that sound like a valid solution?
Update
For now, I am proceeding with testing a solution that uses global maps and unique ids to store objects. C++ would request a new object to be created and only get back an opaque id. Then they can call all of the methods exported as functions, using that id, including requesting for it to be destroyed when done.
If there is a better approach than this, I would love to see an answer. Once I get a fully working prototype, I will add my own answer.
Update #2
I've written a blog post about the final solution that I ended up using: http://justinfx.com/2016/05/14/cpp-bindings-for-go/
The way I ended up solving this, for lack of a better solution, was to use private global maps on the Go side (ref). These maps would associate instances of the Go objects with a random uint64 id, and the id would be returned to C++ as an "opaque handle".
type frameSetMap struct {
lock *sync.RWMutex
m map[FrameSetId]*frameSetRef
rand idMaker
}
//...
func (m *frameSetMap) Add(fset fileseq.FrameSet) FrameSetId {
// fmt.Printf("frameset Add %v as %v\n", fset.String(), id)
m.lock.Lock()
id := FrameSetId(m.rand.Uint64())
m.m[id] = &frameSetRef{fset, 1}
m.lock.Unlock()
return id
}
Then I use reference counting to determine when C++ no longer needs the object, and remove it from the map:
// Go
func (m *frameSetMap) Incref(id FrameSetId) {
m.lock.RLock()
ref, ok := m.m[id]
m.lock.RUnlock()
if !ok {
return
}
atomic.AddUint32(&ref.refs, 1)
// fmt.Printf("Incref %v to %d\n", ref, refs)
}
func (m *frameSetMap) Decref(id FrameSetId) {
m.lock.RLock()
ref, ok := m.m[id]
m.lock.RUnlock()
if !ok {
return
}
refs := atomic.AddUint32(&ref.refs, ^uint32(0))
// fmt.Printf("Decref %v to %d\n", ref, refs)
if refs != 0 {
return
}
m.lock.Lock()
if atomic.LoadUint32(&ref.refs) == 0 {
// fmt.Printf("Deleting %v\n", ref)
delete(m.m, id)
}
m.lock.Unlock()
}
//C++
FileSequence::~FileSequence() {
if (m_valid) {
// std::cout << "FileSequence destroy " << m_id << std::endl;
m_valid = false;
internal::FileSequence_Decref(m_id);
m_id = 0;
m_fsetId = 0;
}
}
And all C++ interactions with the exported Go library communicate via the opaque handle:
// C++
size_t FileSequence::length() const {
return internal::FileSequence_Len(m_id);
}
Unfortunately it does mean that in a multhreaded C++ environment, all threads would go through a mutex to the map. But it is only a write lock when objects are created and destroyed, and for all method calls on an object it is a read lock.

Ensuring each struct has a unique ordinal number

I want to be able to create structs with each having a member that indicates the struct's (not the object's) order. There should be no run-time overhead, and I should be able to use the ordinal at compile-time.
The simples approach doesn't work because for some reason static variables don't work at compile-time:
int nextOrdinal() {
static int ordinal;
return ordinal++;
}
struct S1 {
enum ordinal = nextOrdinal();
}
struct S2 {
enum ordinal = nextOrdinal();
}
How the structs are created isn't important to me at this moment. The problem seems to be that it's not possible to retain a state at compile-time, am I correct?
--Inspired by Boost.units dimensional analysis.
There are no variables at compile-time (excepting the very special case of inside of a CTFE function)--everything must be constant. Further, allowing CTFE variables to go static and pollute the interpreted environment would be a pretty iffy design choice.
Part of the problem is that the compiler doesn't make any guarantees (to my knowledge) about the order of compilation of various code units and may even (in the future) be able to compile pieces in parallel. In general you need to treat compile-time programming as a very strict functional environment with small pockets of flexible mutability (inside CTFE functions). To ensure consistency, CTFE-able functions must be pure and "Ex­e­cuted ex­pres­sions may not ref­er­ence any global or local sta­tic vari­ables." http://dlang.org/function.html#interpretation
In short, I don't think there's any way to have the compiler store this state for you.
I don't know of a reliable way to do this, but if you want to order them based on their location in the source file you could do this:
import std.conv;
import std.stdio;
size_t nextOrdinal(size_t line = __LINE__)()
{
return line;
}
struct S1 {
enum ordinal = nextOrdinal();
}
struct S2 {
enum ordinal = nextOrdinal();
}
void main()
{
writeln(S1.ordinal);
writeln(S2.ordinal);
}
If you have multiple files that call nextOrdinal you could end up with struct definitions which have the same ordinal value. You might consider encoding the file name too:
size_t nextOrdinal(string file = __FILE__, size_t line = __LINE__)()
{
size_t res;
foreach (ch; file)
res += ch;
return res + line;
}

How can I bind a C/C++ structure to Ruby?

I need some advice how can I bind a C/C++ structure to Ruby. I've read some manuals and I found out how to bind class methods to a class, but I still don't understand how to bind structure fields and make them accessible in Ruby.
Here is the code I'm using:
myclass = rb_define_class("Myclass", 0);
...
typedef struct nya
{
char const* name;
int age;
} Nya;
Nya* p;
VALUE vnya;
p = (Nya*)(ALLOC(Nya));
p->name = "Masha";
p->age = 24;
vnya = Data_Wrap_Struct(myclass, 0, free, p);
rb_eval_string("def foo( a ) p a end"); // This function should print structure object
rb_funcall(0, rb_intern("foo"), 1, vnya); // Here I call the function and pass the object into it
The Ruby function seems to assume that a is a pointer. It prints the numeric value of the pointer instead of it's real content (i.e., ["Masha", 24]). Obviously the Ruby function can't recognize this object —I didn't set the object's property names and types.
How can I do this? Unfortunately I can't figure it out.
You have already wrapped your pointer in a Ruby object. Now all you have to do is define how it can be accessed from the Ruby world:
/* Feel free to convert this function to a macro */
static Nya * get_nya_from(VALUE value) {
Nya * pointer = 0;
Data_Get_Struct(value, Nya, pointer);
return pointer;
}
VALUE nya_get_name(VALUE self) {
return rb_str_new_cstr(get_nya_from(self)->name);
}
VALUE nya_set_name(VALUE self, VALUE name) {
/* StringValueCStr returns a null-terminated string. I'm not sure if
it will be freed when the name gets swept by the GC, so maybe you
should create a copy of the string and store that instead. */
get_nya_from(self)->name = StringValueCStr(name);
return name;
}
VALUE nya_get_age(VALUE self) {
return INT2FIX(get_nya_from(self)->age);
}
VALUE nya_set_age(VALUE self, VALUE age) {
get_nya_from(self)->age = FIX2INT(age);
return age;
}
void init_Myclass() {
/* Associate these functions with Ruby methods. */
rb_define_method(myclass, "name", nya_get_name, 0);
rb_define_method(myclass, "name=", nya_set_name, 1);
rb_define_method(myclass, "age", nya_get_age, 0);
rb_define_method(myclass, "age=", nya_set_age, 1);
}
Now that you can access the data your structure holds, you can simply define the high level methods in Ruby:
class Myclass
def to_a
[name, age]
end
alias to_ary to_a
def to_s
to_a.join ', '
end
def inspect
to_a.inspect
end
end
For reference: README.EXT
This is not a direct answer to your question about structures, but it is a general solution to the problem of porting C++ classes to Ruby.
You could use SWIG to wrap C/C++ classes, structs and functions. In the case of a structure, it's burning a house to fry an egg. However, if you need a tool to rapidly convert C++ classes to Ruby (and 20 other languages), SWIG might be useful to you.
In your case involving a structure, you just need to create a .i file which includes (in the simplest case) the line #include <your C++ library.h>.
P.S. Once more, it's not a direct answer to your question involving this one struct, but maybe you could make use of a more general solution, in which case this may help you.
Another option is to use RubyInline - it has limited support for converting C and Ruby types (such as int, char * and float) and it also has support for accessing C structurs - see accessor method in the API.

C/C++: any way to get reflective enums?

I've encountered this situation so many times...
enum Fruit {
Apple,
Banana,
Pear,
Tomato
};
Now I have Fruit f; // banana and I want to go from f to the string "Banana"; or I have string s = "Banana" and from that I want to go to Banana // enum value or int.
So far I've been doing this.. Assuming the enum is in Fruit.h:
// Fruit.cpp
const char *Fruits[] = {
"Apple",
"Banana",
"Pear",
"Tomato",
NULL
};
Obviously that's a messy solution. If a developer adds a new fruit to the header and doesn't add a new entry in Fruits[] (can't blame him, they have to be in two different files!) the application goes boom.
Is there a simple way to do what I want, where everything is in one file? Preprocessor hacks, alien magic, anything..
PS: This, contrary to reflection "for everything", would be really trivial to implement in compilers. Seeing how common a problem it is (at least for me) I really can't believe there is no reflective enum Fruit.. Not even in C++0x.
PS2: I'm using C++ but I tagged this question as C as well because C has the same problem. If your solution includes C++ only things, that's ok for me.
This one requires the fruits to be defined in an external file.
This would be the content of fruit.cpp:
#define FRUIT(name) name
enum Fruit {
#include "fruit-defs.h"
NUM_FRUITS
};
#undef FRUIT
#define FRUIT(name) #name
const char *Fruits [] = {
#include "fruit-defs.h"
NULL
};
#undef FRUIT
And this would be fruit-defs.h:
FRUIT(Banana),
FRUIT(Apple),
FRUIT(Pear),
FRUIT(Tomato),
It works as long as the values start in 0 and are consecutive...
Update: mix this solution with the one from Richard Pennington using C99 if you need non-consecutive values. Ie, something like:
// This would be in fruit-defs.h
FRUIT(Banana, 7)
...
// This one for the enum
#define FRUIT(name, number) name = number
....
// This one for the char *[]
#define FRUIT(name, number) [number] = #name
A c99 way that I've found helps reduce mistakes:
enum Fruit {
APPLE,
BANANA
};
const char* Fruits[] = {
[APPLE] = "APPLE",
[BANANA] = "BANANA"
};
You can add enums, even in the middle, and not break old definitions. You can still get NULL strings for values you forget, of course.
One trick I've done in the past is to add an extra enum and then do a compile time assert (such as Boost's) to make sure the two are kept in sync:
enum Fruit {
APPLE,
BANANA,
// MUST BE LAST ENUM
LAST_FRUIT
};
const char *FruitNames[] =
{
"Apple",
"Banana",
};
BOOST_STATIC_ASSERT((sizeof(FruitNames) / sizeof(*FruitNames)) == LAST_FRUIT);
This will at least prevent someone from forgetting to add to both the enum and the name array and will let them know as soon as they try to compile.
One comment on the macro solution - you don't need a separate file for the enumerators. Just use another macro:
#define FRUITs \
FRUIT(Banana), \
FRUIT(Apple), \
FRUIT(Pear), \
FRUIT(Tomato)
(I would probably leave the commas out, though, and incorporate them into the FRUIT macro as needed.)
There is also Better Enums, which is a head-only library (file) that requires C++11 and is licensed under the BSD software license. Official description:
Reflective compile-time enums for C+: Better Enums is a single, lightweight header file that makes your compiler generate reflective enum types.
Here is the code example from the official website:
#include <enum.h>
BETTER_ENUM(Channel, int, Red = 1, Green, Blue)
Channel c = Channel::_from_string("Red");
const char *s = c._to_string();
size_t n = Channel::_size();
for (Channel c : Channel::_values()) {
run_some_function(c);
}
switch (c) {
case Channel::Red: // ...
case Channel::Green: // ...
case Channel::Blue: // ...
}
Channel c = Channel::_from_integral(3);
constexpr Channel c =
Channel::_from_string("Blue");
It looks very promising, though I haven't tested it yet. In addition there are plenty of (custom) reflection libraries for C++. I hope something similar to Better Enums will be part of the Standard Template Library (STL) (or at least Boost), sooner or later.
What if you did something like this?
enum Fruit {
Apple,
Banana,
NumFruits
};
const char *Fruits[NumFruits] = {
"Apple",
"Banana",
};
Then if you add a new entry to the Fruit enum, your compiler should complain that there are insufficient entries in the initializer of the array, so you would be forced to add an entry to the array.
So it protects you from having the array be the wrong size, but it doesn't help you ensure that the strings are correct.
Take a look at Metaresc library https://github.com/alexanderchuranov/Metaresc
It provides interface for types declaration that will also generate meta-data for the type. Based on meta-data you can easily serialize/deserialize objects of any complexity. Out of the box you can serialize/deserialize XML, JSON, XDR, Lisp-like notation, C-init notation.
Here is a simple example:
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include "metaresc.h"
TYPEDEF_ENUM (fruit_t,
Apple,
Banana,
Pear,
Tomato,
);
int main (int argc, char * argv[])
{
mr_td_t * tdp = mr_get_td_by_name ("fruit_t");
if (tdp)
{
int i;
for (i = 0; i < tdp->fields_size / sizeof (tdp->fields[0]); ++i)
printf ("[%" SCNd64 "] = %s\n", tdp->fields[i].fdp->param.enum_value, tdp->fields[i].fdp->name.str);
}
return (EXIT_SUCCESS);
}
This program will output
$ ./enum
[0] = Apple
[1] = Banana
[2] = Pear
[3] = Tomato
Library works fine for latest gcc and clang.
Could make a class structure for it:
class Fruit {
int value; char const * name ;
protected:
Fruit( int v, char const * n ) : value(v), name(n) {}
public:
int asInt() const { return value ; }
char const * cstr() { return name ; }
} ;
#define MAKE_FRUIT_ELEMENT( x, v ) class x : public Fruit { x() : Fruit( v, #x ) {} }
// Then somewhere:
MAKE_FRUIT_ELEMENT(Apple, 1);
MAKE_FRUIT_ELEMENT(Banana, 2);
MAKE_FRUIT_ELEMENT(Pear, 3);
Then you can have a function that takes a Fruit, and it will even be more type safe.
void foo( Fruit f ) {
std::cout << f.cstr() << std::endl;
switch (f.asInt()) { /* do whatever * } ;
}
The sizeof this is 2x bigger than just an enum. But more than likely that doesn't matter.
As the other people answering the question have shown, there isn't really a clean ("D.R.Y.") way to do this using the C preprocessor alone. The problem is that you need to define an array of size of your enum containing strings corresponding to each enum value, and the C preprocessor isn't smart enough to be able to do that. What I do is to create a text file something like this:
%status ok
%meaning
The routine completed its work successfully.
%
%status eof_reading_content
%meaning
The routine encountered the end of the input before it expected
to.
%
Here %'s mark delimiters.
Then a Perl script, the working part of which looks like this,
sub get_statuses
{
my ($base_name, $prefix) = #_;
my #statuses;
my $status_txt_file = "$base_name.txt";
my $status_text = file_slurp ($status_txt_file);
while ($status_text =~
m/
\%status\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\n
\%meaning\s*(.*?)\s*\n\%\s*\n
/gxs) {
my ($code, $meaning) = ($1, $2);
$code = $prefix."_$code";
$meaning =~ s/\s+/ /g;
push #statuses, [$code, $meaning];
}
return #statuses;
}
reads this file and writes a header file:
typedef enum kinopiko_status {
kinopiko_status_ok,
kinopiko_status_eof_reading_content,
and a C file:
/* Generated by ./kinopiko-status.pl at 2009-11-09 23:45. */
#include "kinopiko-status.h"
const char * kinopiko_status_strings[26] = {
"The routine completed its work successfully.",
"The routine encountered the end of the input before it expected to. ",
using the input file at the top. It also calculates the number 26 here by counting the input lines. (There are twenty-six possible statuses in fact.)
Then the construction of the status string file is automated using make.
I don't like macro solutions, in general, though I admit it's kind of difficult there to avoid them.
Personally I opted for a custom class to wrap my enums in. The goal was to offer a bit more that traditional enums (like iteration).
Under the cover, I use a std::map to map the enum to its std::string counterpart. Then I can use this to both iterate over the enum and "pretty print" my enum or initialize it from a string read in a file.
The problem, of course, is the definition, since I have to first declare the enum and then map it... but that's the price you pay for using them.
Also, I then use not a real enum, but a const_iterator pointing to the map (under the covers) to represent the enum value (with end representing an invalid value).