I am attempting to interface an existing C library to Swift 5.0.1 code on iOS. The C Header files have the following definitions:
char hostname[SFL_MAX_HOSTNAME_CHARS+1];
char os_release[SFL_MAX_OSRELEASE_CHARS+1];
int readHidCounters(HSP *sp, SFLHost_hid_counters *hid, char *hbuf, int hbufLen, char *rbuf, int rbufLen);
typedef struct _HSP {
[Many other elements omitted for brevity]
char hostname[SFL_MAX_HOSTNAME_CHARS+1];
char os_release[SFL_MAX_OSRELEASE_CHARS+1];
} HSP;
readHidCounters has an implementation (edited for brevity) like this:
int readHidCounters(HSP *sp, SFLHost_hid_counters *hid, char *hbuf, int hbufLen, char *rbuf, int rbufLen) {
int gotData = NO;
size_t len = hbufLen;
if(sysctlbyname("kern.hostname", hbuf, &len, NULL, 0) != 0) {
myLog(LOG_ERR, "sysctl(<kern.hostname>) failed : %s", strerror(errno));
}
else {
gotData = YES;
hid->hostname.str = hbuf;
hid->hostname.len = strlen(hbuf);
}
// UUID
memcpy(hid->uuid, sp->uuid, 16);
[...]
}
I have created an HSP struct and attempting to call readHidCounters in Swift like this
var sp = HSP()
[...]
readHidCounters(&sp,
&hidElem.counterBlock.host_hid,
&sp.hostname, // This is the error line
SFL_MAX_HOSTNAME_CHARS,
&sp.os_release,
SFL_MAX_OSRELEASE_CHARS)
I am attempting to pass in &sp.hostname results in the compiler error Cannot convert value of type '(Int8, Int8, Int8, [...], Int8)' to expected argument type 'Int8'. The problem is that hostname is a tuple of Int8's and I can't seem to get it properly converted to char *. I have tried various incarnations of UnsafeMutablePointer, withUnsafeMutablePointer and can't see how to get hostname properly recognized. Any suggestions greatly appreciated!
[Solved]
MartinR pretty much nailed it with his suggestion, but it did have a compiler error: Overlapping accesses to 'sp.hostname', but modification requires exclusive access; consider copying to a local variable. Updated code that compiles is
var myHostName = sp.hostname
var myOsRelease = sp.os_release
let _ = withUnsafeMutablePointer(to: &myHostName) {
$0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout.size(ofValue: sp.hostname)) {
hostNamePtr in
withUnsafeMutablePointer(to: &myOsRelease) {
$0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout.size(ofValue: sp.os_release)) {
osReleasePtr in
readHidCounters(&sp,
&hidElem.counterBlock.host_hid,
hostNamePtr, SFL_MAX_HOSTNAME_CHARS,
osReleasePtr, SFL_MAX_OSRELEASE_CHARS)
}
}
}
}
The “problem” is that C arrays are imported to Swift as tuples, and there is no simple method to treat a tuple as a Swift array, or get a pointer to the element storage (because a tuple can be inhomogeneous).
Similarly as in Converting a C char array to a String one can use the fact that Swift preserves the memory layout of structures imported from C, and with some pointer juggling and rebinding you'll get
let result = withUnsafeMutablePointer(to: &sp.hostname) {
$0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout.size(ofValue: sp.hostname)) {
hostNamePtr in
withUnsafeMutablePointer(to: &sp.os_release) {
$0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout.size(ofValue: sp.os_release)) {
osReleasePtr in
readHidCounters(&sp,
&hidElem.counterBlock.host_hid,
hostNamePtr, SFL_MAX_HOSTNAME_CHARS,
osReleasePtr, SFL_MAX_OSRELEASE_CHARS)
}
}
}
}
Another “trick” is to define C helper functions which return the array addresses as a pointers, and use the
SE-0044 Import as member
feature to make these helper functions available to Swift as computed properties. In the bridging header file you have to add
__attribute__((swift_name("getter:HSP.hostNamePtr(self:)")))
static inline char * _Nonnull hostNamePtr(HSP * _Nonnull hsp)
{
return hsp->hostname;
}
__attribute__((swift_name("getter:HSP.osReleasePtr(self:)")))
static inline char * _Nonnull osReleasePtr(HSP * _Nonnull hsp)
{
return hsp->os_release;
}
and then you can use these easily from Swift:
var sp = HSP()
let result = readHidCounters(&sp,
&hidElem.counterBlock.host_hid,
sp.hostNamePtr, SFL_MAX_HOSTNAME_CHARS,
sp.osReleasePtr, SFL_MAX_OSRELEASE_CHARS)
Try removing the & from the call, sp.hostname is already a pointer because it is an array, if it works you will have to remove the & from &sp.os_release too
Related
I am trying to make a Ruby module using the C API.
I must admit, I am having trouble fully understanding the documentation online for it, but I am trying to return a Ruby Object using data from a C Structure from another class method (sorry if that doesn't make sense). Here is an example of my problem:
example.c
#include "ruby.h"
#include "extconf.h"
typedef struct example1_t
{
int x;
} example1_t;
typedef struct example2_t
{
char *name;
} example2_t;
void example1_free(example1_t *e1);
void example2_free(example2_t *e2);
static VALUE rb_example1_alloc(VALUE klass)
{
return Data_Wrap_Struct(klass, NULL, example1_free, ruby_xmalloc(sizeof(example1_t)));
}
static VALUE rb_example1_init(VALUE self, VALUE x)
{
example1_t *e1;
Check_Type(x, T_FIXNUM);
Data_Get_Struct(self, example1_t, e1);
e1->x = NUM2INT(x);
return self;
}
static VALUE rb_example1_x(VALUE self)
{
example1_t *e1;
Data_Get_Struct(self, example1_t, e1);
return INT2NUM(e1->x);
}
static VALUE rb_example2_alloc(VALUE klass)
{
return Data_Wrap_Struct(klass, NULL, example2_free, ruby_xmalloc(sizeof(example2_t)));
}
static VALUE rb_example2_init(VALUE self, VALUE s)
{
example2_t *e2;
Check_Type(s, T_STRING);
Data_Get_Struct(self, example2_t, e2);
e2->name = (char*)malloc(RSTRING_LEN(s) + 1);
memcpy(e2->name, StringValuePtr(s), RSTRING_LEN(s) + 1);
return self;
}
static VALUE rb_example2_name(VALUE self)
{
example2_t *e2;
Data_Get_Struct(self, example2_t, e2);
return rb_str_new_cstr(e2->name);
}
static VALUE rb_example2_name_len(VALUE self)
{
example1_t *len;
example2_t *e2;
Data_Get_Struct(self, example2_t, e2);
len->x = strlen(e2->name);
/*
How do I make a new Example1 Ruby Class from the "len"
structure and return it with the length of e2->name
assigned to len->x?
*/
return it?
}
void Init_example()
{
VALUE mod = rb_define_module("Example");
VALUE example1_class = rb_define_class_under(mod, "Example1", rb_cObject);
VALUE example2_class = rb_define_class_under(mod, "Example2", rb_cObject);
rb_define_alloc_func(example1_class, rb_example1_alloc);
rb_define_alloc_func(example2_class, rb_example2_alloc);
rb_define_method(example1_class, "initialize", rb_example1_init, 1);
rb_define_method(example1_class, "x", rb_example1_x, 0);
rb_define_method(example2_class, "initialize", rb_example2_init, 1);
rb_define_method(example2_class, "name", rb_example2_name, 0);
rb_define_method(example2_class, "name_len", rb_example2_name_len, 0);
}
void example1_free(example1_t *e1)
{
memset(e1, 0, sizeof(example1_t));
}
void example2_free(example2_t *e2)
{
memset(e2, 0, sizeof(example2_t));
}
As you can see in the rb_example2_name_len, I would like to create an Example1 class and return that from an Example2 method. How would I be able to do this?
Any help is much appreciated.
You can use rb_class_new_instance to create new objects.
You will need the VALUE representing the class object. One way to get this would be to store it in a global or static variable and then initialize it in the init function rather than just having a local variable:
static VALUE example1_class;
//...
void Init_example()
{
//...
example1_class = rb_define_class_under(mod, "Example1", rb_cObject);
//...
}
You will also need to convert the arguments to Ruby form. rb_class_new_instance takes an array of VALUES.
static VALUE rb_example2_name_len(VALUE self)
{
example2_t *e2;
Data_Get_Struct(self, example2_t, e2);
// Format arguments as array of VALUES
VALUE args[1];
args[0] = INT2NUM((int)strlen(e2->name));
// args are length of array, pointer to array and class you are
// creating an instance of. example1_class is available here because
// we made it a static variable.
VALUE e1 = rb_class_new_instance(1, args, example1_class);
return e1;
}
An alternative to making example1_class a static would be to use rb_const_get. In this case you would have to get the containing module as well:
VALUE mExample = rb_const_get(rb_cObject, rb_intern("Example"));
VALUE cExample1 = rb_const_get(mExample, rb_intern("Example1"));
VALUE e1 = rb_class_new_instance(1, args, cExample1);
rb_class_new_instance basically just calls your allocation function followed by your initializer, so you could just reproduce that code yourself inside rb_example2_name_len:
example1_t *e1_struct = ruby_xmalloc(sizeof(example1_t));
e1_struct->x = (int)strlen(e2->name);
VALUE e1 = Data_Wrap_Struct(example1_class, NULL, example1_free, e1_struct);
return e1;
This would avoid needing to convert the data to ruby format just for it to be converted back straight away, but I don’t think you would gain much and using rb_class_new_instance is probably clearer.
You should also be aware that the Data_Wrap_Struct struct macros have been deprecated:
The old (non-Typed) Data_XXX macro family has been deprecated. In the future version of Ruby, it is possible old macros will not work.
I’ve used them here for simplicity but you might want to loo into using the newer TypedData_ macros.
I am new to C and can't yet freely navigate trough my program memory. Anyways, I am creating a static memory data type (gc_menu) that should hold a pointer to created at execution time structure (mcl_items).
For simplicity mcl_items structure have one virtual method (push) that is going to be run inside of gc_menu_add_item and also assigned to the gc_menu static space. push saves an menu item name (letter) and method to mcl_item virtual object.
mcl_items.h code:
[...]
typedef struct Items_t {
int8_t size;
char names[64];
void (*methods[64])();
// Interface
void (*push)(struct Items_t *self, char c, void (*method)());
}mcl_items;
mcl_items *new_mcl_items();
void mcl_items_push(mcl_items *self, char c, void (*method)());
mcl_items.c code:
[...]
#include "mcl_items.h"
mcl_items *new_mcl_items() {
fprintf(stderr, "MCL_Items: Generating a new set of mcl_items..");
// Build a virtual object
mcl_items *items = calloc(1, sizeof(struct Items_t));
items->push = mcl_items_push;
// Set data
items->size = 0;
return items;
}
void mcl_items_push(mcl_items *self, char c, void (*method)()) {
fprintf(stderr, "MCL_Items: pushing a new item..");
self->names[self->size] = c;
self->methods[self->size] = method;
self->size ++;
}
gc_menu.h code:
#include "items.h"
typedef struct {
// Interface
void (*add_item)(char c, void (*method)());
// Data
mcl_items *items;
}__gc_menu;
extern __gc_menu const gc_menu;
gc_menu.c code:
static void gc_menu_add_item(char c, void (*method)) {
fprintf(stderr, "GC_Menu: Passing an new item..");
fprintf(stderr, "length = %i\n", gc_menu.items->size);
gc_menu.items->push(gc_menu.items, c, method);
}
__gc_menu const gc_menu = {gc_menu_add_item, // Virtual methods
new_mcl_items}; // Data
After callng gc_menu.add_item the segmentation fault occurs and gc_menu.items->size is equal to 72, not 0 as is defined in the definition of new_mcl_items.
main.c code:
gc_menu.add_item('q', xw->end(xw));
GC_Menu: Passing an new item..length = 72
[1] 66021 segmentation fault (core dumped) ./3D_scean
So what am I doing wrong? Why is there such a weird data written to instances of my gc_menu.items?
You've initialized gc_menu.items to new_mcl_items, i.e. a pointer to the function new_mcl_items (which should give you a warning since it is of type mcl_items *(*)(void) and not mcl_items *).
It looks like what you want is to actually call the function new_mcl_items() and set gc_menu.items to the value that new_mcl_items() returns. You can't do this with an initializer; initializers of global or static objects must be known at compile or link time. Standard C doesn't have "constructors".
So you'll have to remove the const from the declaration and definition of gc_menu, and add code to main (or some function called by main, etc) to initialize gc_menu.items at run time.
gc_menu.h:
extern __gc_menu gc_menu;
gc_menu.c:
__gc_menu gc_menu = {
gc_menu_add_item,
NULL // or whatever else you like
};
main.c or whatever you have called it:
int main(void) {
// ...
gc_menu.items = new_mcl_items();
// ...
}
I have got a choice to do to initialize a big structure in C.
I am working on an embedded micro with a tiny memory size.
I've got the chip configuration saved in EEPROM.
So I have a struct which contain all the configuration page in EEPROM :
Typedef struct
{
unsigned int Param1;
float Param2;
unsigned char Param3;
[...]
char Paramx[SIZE];
} T_EEPROM;
We have to keep in mind that this struct is heavy regarding the tiny memory size of the micro.
I have a global variable of this type :
T_EEPROM MyConfig;
This is used to modify or access EEPROM configuration :
MyConfig.Param1 = NewValue;
WriteEEPROM(MyConfig);
Now I want to initialize this variable with diffrents kind of factory configurations (CONFIG A, CONFIG B, etc)
All the parameters for each factory configuration can be defined by a #define
After that, I don't know which method to use :
1) Write an initialization function which take all the values in parameters :
bool InitEEPROM(unsigned int param1, float param2, unsigned char param3, [...], char *Paramx)
{
MyConfig.Param1 = param1;
MyConfig.Param2 = param2;
MyConfig.Param3 = param3;
[...]
MyConfig.Paramx = paramx;
}
After, I could call the function lake that :
void InitFactoryEEPROM (unsigned char type)
{
if (type == 1)
InitEEPROM(DEFINE_PARAM1_CONFIG_1, DEFINE_PARAM2_CONFIG_1,DEFINE_PARAM3_CONFIG_1, [...], DEFINE_PARAMx_CONFIG_1);
else if (type == 2)
InitEEPROM(DEFINE_PARAM1_CONFIG_2, DEFINE_PARAM2_CONFIG_2,DEFINE_PARAM3_CONFIG_2, [...], DEFINE_PARAMx_CONFIG_2);
else if (type == 3)
[...]
}
Disadventage: heavy to write
2) Create a big array with all the factory configurations :
T_EEPROM FactoryEepromConfig[CONFIG_COUNT] =
{
{DEFINE_PARAM1_CONFIG_1, DEFINE_PARAM2_CONFIG_1, DEFINE_PARAM3_CONFIG_1, [...], DEFINE_PARAMx_CONFIG_1},
{DEFINE_PARAM1_CONFIG_2, DEFINE_PARAM2_CONFIG_2,DEFINE_PARAM3_CONFIG_2, [...], DEFINE_PARAMx_CONFIG_2},
[...]
};
With an easier initialisation function:
bool InitEEPROM(T_EEPROM factoryConfig)
{
MyConfig.Param1 = factoryConfig.Param1 ;
MyConfig.Param2 = factoryConfig.Param2;
MyConfig.Param3 = factoryConfig.Param3;
[...]
MyConfig.Paramx = factoryConfig.Paramx;
}
And this call:
void InitFactoryEEPROM (unsigned char type)
{
InitEEPROM(FactoryEepromConfig[type]);
}
Disadventage:
Very heavy in memory because I have a T_EEPROM instance for each factory configuration.
Anyone has got a better idea?
In all the scenario's (possibilities) you gave, the values need to be in memory, either as variables or as values to initialize variables with. So there is not much difference in the memory footprint. Using initialization functions has the overhead of code bytes required to execute the initialization.
Having one static array containing all the values, and that you index every time you need a value, has the overhead of instructions to index the array. Copying the values of an array index to a "working set" variable has the overhead of the extra variable.
Possibly you can measure which is smallest by making several versions, e.g.:
one static array that is indexed for each parameter access;
one static array and copying the working set to an extra variable;
initializing a working set variable using an initialization function.
But this assumes the working set of values can change during execution. If they don;t change, then you can use #defines to select the working set of values and use that for static initialization of the working set variables.
This is short and clean:
static const T_EEPROM FactoryEepromConfig[CONFIG_COUNT] =
{
{DEFINE_PARAM1_CONFIG_1, DEFINE_PARAM2_CONFIG_1, DEFINE_PARAM3_CONFIG_1, [...], DEFINE_PARAMx_CONFIG_1},
{DEFINE_PARAM1_CONFIG_2, DEFINE_PARAM2_CONFIG_2,DEFINE_PARAM3_CONFIG_2, [...], DEFINE_PARAMx_CONFIG_2},
[...]
};
void InitFactoryEEPROM (size_t type)
{
assert(type < CONFIG_COUNT);
MyConfig = FactoryEepromConfig[type];
}
to avoid globals you can change the function to this:
void InitFactoryEEPROM (T_EEPROM* config, size_t type)
{
assert(type < CONFIG_COUNT);
*config = FactoryEepromConfig[type];
}
I'm trying to send an image from C++ to C# with an interop (marshaling) of C++ managed. image->getStream() return a const char* from a string.
I'm having exception with my Marshal::Copy function.
An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Am I doing the right thing for the copy from a const char* to a byte array ? My dll is compiled with ASCII char set in VS2010.
array<System::Byte>^ OsgViewer::getLastImage()
{
array< Byte >^ byteArray;
m_ImageQueue->lock();
int index = m_ImageQueue->getCurrentImageIndex();
std::shared_ptr<Image> image = m_ImageQueue->getImage(static_cast<unsigned int>(index));
if( image && image->isValid() == true)
{
int wLen = image->getStreamSize();
char* wStream = const_cast<char*>(image->getStream());
byteArray = gcnew array< Byte >(wLen);
// convert native pointer to System::IntPtr with C-Style cast
Marshal::Copy((IntPtr)wStream ,byteArray , 0, wLen);
}
m_ImageQueue->unlock();
return byteArray;
}
Image is a home made C++ class
class ADAPTER Image
{
public :
Image();
~Image();
const char* getStream() const;
int getStreamSize();
bool setStringStream(std::ostringstream* iStringStream);
void setIsValid(bool isValid){ m_isValid = isValid;}
bool isValid() const{return m_isValid;}
std::ostringstream* getOStringStream() {return m_StringStream;}
private:
std::ostringstream* m_StringStream;
bool m_isValid;
};
I wouldn't use Marshal::Copy. Since you have the array locally, why not just pin it and use memcpy?
pin_ptr<Byte> ptrBuffer = &byteArray[byteArray->GetLowerBound(0)];
You can now call memcpy to ptrBuffer.
When the scope ends the pinning is automatically undone.
static struct dll_wifi_state **dll_states;
enum dll_type {
DLL_UNSUPPORTED,
DLL_ETHERNET,
DLL_WIFI
};
struct dll_state {
enum dll_type type;
union {
struct dll_eth_state *ethernet;
struct dll_wifi_state *wifi;
} data;
};
static struct dll_state *dll_states = NULL;
struct dll_wifi_state {
int link;
// A pointer to the function that is called to pass data up to the next layer.
up_from_dll_fn_ty nl_callback;
bool is_ds;
};
This is the method whose pointer is being passed in the dll_wifi_state struct.
static void up_from_dll(int link, const char *data, size_t length)
{
//some code here
}
In other file, I am calling this method
void reboot_accesspoint()
{
// We require each node to have a different stream of random numbers.
CNET_srand(nodeinfo.time_of_day.sec + nodeinfo.nodenumber);
// Provide the required event handlers.
CHECK(CNET_set_handler(EV_PHYSICALREADY, physical_ready, 0));
// Prepare to talk via our wireless connection.
CHECK(CNET_set_wlan_model(my_WLAN_model));
// Setup our data link layer instances.
dll_states = calloc(nodeinfo.nlinks + 1, sizeof(struct dll_state));
for (int link = 0; link <= nodeinfo.nlinks; ++link) {
switch (linkinfo[link].linktype) {
case LT_LOOPBACK:
dll_states[link].type = DLL_UNSUPPORTED;
break;
case LT_WAN:
dll_states[link].type = DLL_UNSUPPORTED;
break;
case LT_LAN:
dll_states[link].type = DLL_ETHERNET;
dll_states[link].data.ethernet = dll_eth_new_state(link, up_from_dll);
break;
case LT_WLAN:
dll_states[link].type = DLL_WIFI;
dll_states[link].data.wifi = dll_wifi_new_state(link,
up_from_dll,
true /* is_ds */);
break;
}
}
// printf("reboot_accesspoint() complete.\n");
}
It works fine like this, but I want to add another argument i.e. up_from_dll((int link, const char *data, size_t length, int seq). And as soon as I add this argument, following error starts coming up
ap.c:153: warning: passing argument 2 of ‘dll_wifi_new_state’ from incompatible pointer type
Is there a way of adding another argument to that method without getting error ??? I am really bad with pointers :(
Any help would be much appreciated.
Line 153 :
dll_states[link].data.wifi = dll_wifi_new_state(link,
up_from_dll,
true /* is_ds */);
And method
struct dll_wifi_state *dll_wifi_new_state(int link,
up_from_dll_fn_ty callback,
bool is_ds)
{
// Ensure that the given link exists and is a WLAN link.
if (link > nodeinfo.nlinks || linkinfo[link].linktype != LT_WLAN)
return NULL;
// Allocate memory for the state.
struct dll_wifi_state *state = calloc(1, sizeof(struct dll_wifi_state));
// Check whether or not the allocation was successful.
if (state == NULL)
return NULL;
// Initialize the members of the structure.
state->link = link;
state->nl_callback = callback;
state->is_ds = is_ds;
return state;
}
I haven't changed anything else apart from adding the new parameter to up_from_dll.
The second parameter to dll_wifi_new_state is up_from_dll_fn_ty callback.
It's not in your code listing right now, but up_from_dll_fn_ty is a typedef saying that the up_from_dll_fn_ty is a function pointer with specific parameters (which don't include int seq)
When you updated up_from_dll with different parameters, it no longer matches the type specified by up_from_dll_fn_ty and expected as the second parameter for dll_wifi_new_state. You'll need to add the parameter to up_from_dll_fn_ty and you should be good.
If you post the definition of up_from_dll_fn_ty, it would make the question have all the information and allow me to help you more if you still need it.
You're looking for something like:
typedef void (*up_from_dll_fn_ty)(int link, const char *data, size_t length);
and change it to
typedef void (*up_from_dll_fn_ty)(int link, const char *data, size_t length, int seq);
Here's a link to a question that has good information about creating typedefs for function pointers:
Understanding typedefs for function pointers in C