Wondering if you can do something like this to store arbitrary objects in an array in C:
void *arr[123];
int len = 0;
void
pusharr(void *object) {
arr[len++] = &object;
}
int
main() {
char *foo = "foo"
pusharr(1)
pusharr("foo")
pusharr(&foo)
pusharr(foo)
pusharr(somestruct)
pusharr(someotherstructtype)
pusharr(afunction)
pusharr(anythingbasically)
pusharr(true)
pusharr(NULL)
// arr[4] == somestruct, etc.
}
Basically I'm trying to model like the free(void *ptr) function and pass generic pointers to any possible object type into the function, so it can save references to them. Wondering if that's possible, and if not this way then how.
In terms of functions, it's like this...
So there is this which shows how to pass in a void pointer to get arbitrary types out of a function.
void foo(char* szType, void *pOut) {
switch (szType[0]) {
case 'I': *(int*)pOut = 1; break;
case 'F': *(float*)pOut = 1; break;
}
}
int a;
float b;
foo("I", &a);
foo("F", &b);
I'm wondering if there is a way to do this but attach it to an object/struct.
struct mydataobject {
void *value;
}
This way you could have the function at least return a type.
mydataobject
foo() {
}
In my case I want to have 2 functions, push and pop that work on arbitrary data.
void
mypush(mydataobject something) {
arr[index++] = something
}
mydataobject
mypop() {
return arr[index--]
}
mydataobject a = { "foo" }
mydataobject b = { 123 }
mydataobject c = { true }
mydataobject d = { a }
// it should work with arbitrary data.
Wondering if anything like this is possible.
Yes, this is possible. Just very difficult to do correctly.
Think of scripting languages like Perl, Python or Javascript. Each of those use variables that can hold different types of values. Each of those scripting languages is written in C.
So how do they do it?
Generally they use unions and type tags. A type tag is often an integer like you're using as szType. Sometimes they are a pointer to a structure with data about the type. Sometimes it is a combination, because integer tags are all below 0x1000 (for example) so any number larger must be a pointer.
So design a C union that can hold data about all of your data types. Include a pointer so that extra-large types do not have to make every type huge. Then design a struct that holds a type tag and one of your unions.
Then for every function you create to manipulate these structs, check the type tags and do the correct operations for each one.
I was bored. Here is some code. Note that this is C99 so it won't compile in older versions of Visual Studio (VS 2017 worked!). I used gcc and clang to compile it. Tested with valgrind so no memory leaks. After building it with
gcc -Wall -Wextra -Werror -pedantic -g -O0 type-union-test.c -o type-union-test
run it with
./type-union-test 11 bb 22333333 dd 10 a 11 b
And the code for type-union-test.c: (also available at https://github.com/zlynx/type-union-test )
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// log10(2^64) + 2
#define MAX_INTSTRING_LEN 21
enum VAL_types {
VAL_UNDEFINED,
VAL_INT32,
VAL_STRING,
VAL_OBJECT,
};
enum OPS_RESULT_errors {
OPS_RESULT_OK,
OPS_RESULT_FALSE,
OPS_RESULT_UNIMPLEMENTED,
OPS_RESULT_INVALID_TYPE,
OPS_RESULT_INVALID_INTEGER,
};
struct VAL;
struct OBJECT;
union VAL_type_data {
int32_t int32;
char *string;
struct OBJECT *object;
};
typedef struct OPS_RESULT {
struct VAL *val;
enum OPS_RESULT_errors error;
} OPS_RESULT;
typedef struct VAL_OPS {
OPS_RESULT (*set_type)(struct VAL *, enum VAL_types);
OPS_RESULT (*copy_from_int32)(struct VAL *, int32_t);
OPS_RESULT (*copy_from_string)(struct VAL *, const char *);
OPS_RESULT (*move_from_key_val)(struct VAL *, struct VAL *, struct VAL *);
OPS_RESULT (*is_equal)(struct VAL *, struct VAL *);
OPS_RESULT (*debug_print)(struct VAL *);
} VAL_OPS;
typedef struct VAL {
enum VAL_types type_id;
size_t ref_count;
union VAL_type_data type_data;
const VAL_OPS *ops;
bool constant;
bool owned_ptr;
} VAL;
typedef struct OBJECT_KV {
VAL *key;
VAL *val;
} OBJECT_KV;
typedef struct OBJECT {
OBJECT_KV *array;
size_t len;
size_t cap;
} OBJECT;
OBJECT *OBJECT_new(void);
void OBJECT_delete(OBJECT *op);
VAL *VAL_new(void);
void VAL_delete(VAL *vp);
bool result_ok(OPS_RESULT res) { return res.error == OPS_RESULT_OK; }
const char *result_error_str(enum OPS_RESULT_errors err) {
switch (err) {
case OPS_RESULT_OK:
return "OK";
case OPS_RESULT_FALSE:
return "false";
case OPS_RESULT_UNIMPLEMENTED:
return "unimplemented";
case OPS_RESULT_INVALID_TYPE:
return "invalid type";
case OPS_RESULT_INVALID_INTEGER:
return "invalid integer";
default:
return "unknown error";
}
}
void result_print(OPS_RESULT res) {
FILE *out = stdout;
fprintf(out, "{error: \"%s\"", result_error_str(res.error));
if (result_ok(res) && res.val) {
res.val->ops->debug_print(res.val);
}
fprintf(out, "}");
}
VAL *result_unwrap(OPS_RESULT res) {
if (res.error != OPS_RESULT_OK) {
result_print(res);
printf("\n");
fflush(stdout);
abort();
}
return res.val;
}
void *xmalloc(size_t bytes) {
void *p = malloc(bytes);
if (!p)
abort();
return p;
}
void xfree(void *p) { free(p); }
void xrealloc(void **p, size_t bytes) {
void *new_p = realloc(*p, bytes);
if (!new_p)
abort();
*p = new_p;
}
// Got to take into account the virtual functions we are not using yet!
// One val may have reimplemented is_equal so check both ways. For SCIENCE!
// And unnecessary complexity!
OPS_RESULT VAL_is_equal(VAL *v1_p, VAL *v2_p) {
if (result_ok(v1_p->ops->is_equal(v1_p, v2_p)) &&
result_ok(v2_p->ops->is_equal(v2_p, v1_p)))
return (OPS_RESULT){.error = OPS_RESULT_OK};
return (OPS_RESULT){.error = OPS_RESULT_FALSE};
}
OPS_RESULT VAL_default_set_type(VAL *vp, enum VAL_types type_id) {
if (vp->type_id != VAL_UNDEFINED && vp->type_id != type_id)
// Would need to implement type conversion.
return (OPS_RESULT){.error = OPS_RESULT_UNIMPLEMENTED};
vp->type_id = type_id;
switch (type_id) {
case VAL_OBJECT:
vp->type_data.object = OBJECT_new();
break;
default:
// Do nothing special.
break;
}
return (OPS_RESULT){.error = OPS_RESULT_OK};
}
OPS_RESULT VAL_default_copy_from_int32(VAL *vp, int32_t source) {
int r;
switch (vp->type_id) {
case VAL_INT32:
vp->type_data.int32 = source;
break;
case VAL_STRING:
if (vp->type_data.string)
xfree(vp->type_data.string);
vp->type_data.string = xmalloc(MAX_INTSTRING_LEN);
r = snprintf(vp->type_data.string, MAX_INTSTRING_LEN, "%d", source);
if (r >= MAX_INTSTRING_LEN)
abort();
break;
default:
return (OPS_RESULT){.error = OPS_RESULT_INVALID_TYPE};
}
return (OPS_RESULT){.error = OPS_RESULT_OK};
}
OPS_RESULT VAL_default_copy_from_string(VAL *vp, const char *s) {
int r;
char *cp;
long lval;
switch (vp->type_id) {
case VAL_INT32:
errno = 0;
lval = strtol(s, &cp, 0);
if (errno == ERANGE || !(*cp == '\0' || isspace(*cp)) ||
!(lval <= INT_MAX && lval >= INT_MIN))
return (OPS_RESULT){.error = OPS_RESULT_INVALID_INTEGER};
vp->type_data.int32 = lval;
break;
case VAL_STRING:
if (vp->type_data.string)
xfree(vp->type_data.string);
r = strlen(s);
vp->type_data.string = xmalloc(r + 1);
strcpy(vp->type_data.string, s);
break;
default:
return (OPS_RESULT){.error = OPS_RESULT_INVALID_TYPE};
}
return (OPS_RESULT){.error = OPS_RESULT_OK};
}
// This is a move because it does not increment reference counts of key or val.
OPS_RESULT VAL_default_move_from_key_val(VAL *vp, VAL *key, VAL *val) {
// Must be an OBJECT
if (vp->type_id != VAL_OBJECT)
return (OPS_RESULT){.error = OPS_RESULT_INVALID_TYPE};
// Find existing key
size_t i;
for (i = 0; i < vp->type_data.object->len; i++) {
if (result_ok(VAL_is_equal(vp->type_data.object->array[i].key, key))) {
// Delete existing key and value
VAL_delete(vp->type_data.object->array[i].key);
VAL_delete(vp->type_data.object->array[i].val);
break;
}
}
// Insert new key and value
if (i == vp->type_data.object->len) {
// Might have to realloc.
if (i == vp->type_data.object->cap) {
if (vp->type_data.object->cap > 0)
vp->type_data.object->cap *= 2;
else
vp->type_data.object->cap = 4;
xrealloc((void **)&vp->type_data.object->array,
vp->type_data.object->cap * sizeof *vp->type_data.object->array);
}
vp->type_data.object->len++;
}
vp->type_data.object->array[i].key = key;
vp->type_data.object->array[i].val = val;
return (OPS_RESULT){.error = OPS_RESULT_OK};
}
OPS_RESULT VAL_default_is_equal(VAL *v1_p, VAL *v2_p) {
// Not going to do type conversion right now.
if (v1_p->type_id != v2_p->type_id)
return (OPS_RESULT){.error = OPS_RESULT_UNIMPLEMENTED};
switch (v1_p->type_id) {
case VAL_INT32:
if (v1_p->type_data.int32 != v2_p->type_data.int32)
return (OPS_RESULT){.error = OPS_RESULT_FALSE};
break;
case VAL_STRING:
if (strcmp(v1_p->type_data.string, v2_p->type_data.string) != 0)
return (OPS_RESULT){.error = OPS_RESULT_FALSE};
break;
default:
// Not going to compare OBJECTS right now. Too hard.
return (OPS_RESULT){.error = OPS_RESULT_UNIMPLEMENTED};
}
return (OPS_RESULT){.error = OPS_RESULT_OK};
}
OPS_RESULT VAL_default_debug_print(VAL *vp) {
FILE *out = stdout;
size_t i;
switch (vp->type_id) {
case VAL_INT32:
fprintf(out, "%d", vp->type_data.int32);
break;
case VAL_STRING:
fprintf(out, "\"%s\"", vp->type_data.string);
break;
case VAL_OBJECT:
fprintf(out, "{");
for (i = 0; i < vp->type_data.object->len; i++) {
if (i > 0)
fprintf(out, ", ");
vp->type_data.object->array[i].key->ops->debug_print(
vp->type_data.object->array[i].key);
fprintf(out, ": ");
vp->type_data.object->array[i].val->ops->debug_print(
vp->type_data.object->array[i].val);
}
fprintf(out, "}");
break;
default:
fprintf(out, "\"undefined type\"");
break;
}
return (OPS_RESULT){.error = OPS_RESULT_OK};
}
static const VAL_OPS VAL_OPS_template = {
.set_type = VAL_default_set_type,
.copy_from_int32 = VAL_default_copy_from_int32,
.copy_from_string = VAL_default_copy_from_string,
.move_from_key_val = VAL_default_move_from_key_val,
.is_equal = VAL_default_is_equal,
.debug_print = VAL_default_debug_print,
};
static const VAL VAL_template = {.type_id = VAL_UNDEFINED,
.ref_count = 1,
.type_data = {0},
.ops = &VAL_OPS_template,
.constant = false,
.owned_ptr = false};
VAL *VAL_new(void) {
VAL *p = xmalloc(sizeof *p);
*p = VAL_template;
return p;
}
void VAL_delete(VAL *vp) {
if (--vp->ref_count == 0) {
switch (vp->type_id) {
case VAL_STRING:
xfree(vp->type_data.string);
break;
case VAL_OBJECT:
OBJECT_delete(vp->type_data.object);
break;
default:
// Do nothing.
break;
}
xfree(vp);
}
}
static const OBJECT OBJECT_template = {0};
OBJECT *OBJECT_new(void) {
OBJECT *p = xmalloc(sizeof *p);
*p = OBJECT_template;
return p;
}
void OBJECT_delete(OBJECT *op) {
for (size_t i = 0; i < op->len; i++) {
VAL_delete(op->array[i].key);
VAL_delete(op->array[i].val);
}
xfree(op->array);
xfree(op);
}
int main(int argc, char *argv[]) {
VAL *top = VAL_new();
result_unwrap(top->ops->set_type(top, VAL_OBJECT));
for (int i = 1; i < argc - 1; i += 2) {
VAL *key = VAL_new();
VAL *val = VAL_new();
result_unwrap(key->ops->set_type(key, VAL_INT32));
// key->ops->copy_from_int32(key, i);
result_unwrap(key->ops->copy_from_string(key, argv[i]));
result_unwrap(val->ops->set_type(val, VAL_STRING));
// val->ops->copy_from_string(val, argv[i]);
result_unwrap(val->ops->copy_from_string(val, argv[i + 1]));
result_unwrap(top->ops->move_from_key_val(top, key, val));
}
top->ops->debug_print(top);
printf("\n");
VAL_delete(top);
return 0;
}
Store union Objects
A union lets you hold one of several different types. If the first member is some kind of enum that declares which member of the union is active, that’s called a discriminated union.
Store void Pointers
A void* can reference any type of object, so if the objects themselves exist outside the array, the array can hold pointers to them. You still need some way to remember what the type of the objects was, such as a structure containing both an enum and a void*.
Store Arrays of char
An array of char or unsigned char can hold the object representation of any object its size or smaller. Make sure that the arrays are aligned to max_align_t to guarantee that they have the correct alignment to store any datatype, or just specify multiple types that are required to be correctly aligned. You could cast the char* to the correct type of pointer.
Using a union handles this all for you..
I am beginning to implement a generic memory pool. This is for learning purposes so, there are tons of mistakes definitely. But, I was moving forward. Now I am stucked at a new part. First, the code
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
typedef enum { FALSE, TRUE } BOOL;
typedef struct mem_block {
uint8_t* data;
size_t block_size;
size_t pool_position;
BOOL is_freed;
} mem_block;
typedef struct mem_pool {
mem_block* blocks;
size_t index;
size_t pool_size;
} mem_pool;
mem_pool *pool_init() {
mem_pool *pool = (mem_pool *) malloc(sizeof(mem_pool));
pool->pool_size = (size_t) 128;
mem_block* blk = (mem_block *) malloc(pool->pool_size * sizeof (mem_block));
pool->index = 0;
pool->blocks = blk;
return pool;
}
void *pool_allocate(mem_pool **pool, size_t size) {
mem_pool* _pool = *pool;
size_t free_portion = _pool->pool_size - _pool->index;
if(size < free_portion){
mem_block* allocated_blk = _pool->blocks + _pool->index;
uint8_t* data = (uint8_t*) malloc(size * sizeof(uint8_t));
allocated_blk->data = data;
allocated_blk->block_size = size;
allocated_blk->is_freed = FALSE;
allocated_blk->pool_position = _pool->index;
_pool->index += size;
return (void *) allocated_blk->data;
}
else{
printf("Pool is out of memory");
return NULL;
}
}
/*void pool_free(mem_pool **pool, void *block) {
mem_block* cur = (mem_block*) block;
mem_block* next = cur + 1;
// override the unneeded memory
memmove(cur, next, (*pool)->pool_size - next->pool_position);
}*/
typedef struct complex {
double i;
double r;
} complex;
mem_pool *GLOBAL_POOL = pool_init();
int main() {
complex *c1 = (complex *) pool_allocate(&GLOBAL_POOL, sizeof(complex));
c1->r = 1.0;
c1->i = 2.0;
printf("Value is (%f + %fi)\n", c1->r, c1->i);
printf("Remaining free size is %ld\n", GLOBAL_POOL->pool_size - GLOBAL_POOL->index);
complex *c2 = (complex *) pool_allocate(&GLOBAL_POOL, sizeof(complex));
c2->r = 2.0;
c2->i = 3.0;
printf("Value is (%f + %fi)\n", c2->r, c2->i);
printf("Remaining free size is %ld\n", GLOBAL_POOL->pool_size - GLOBAL_POOL->index);
mem_block* cur = (mem_block *) &c2;
printf("Position of c2 is %ld\n", cur->pool_position);
printf("Adress of c2's block is %x\n", cur);
printf("Address of c2 is %x\n", &c2);
printf("c2 points to %x\n", c2);
complex *c3 = (complex *) pool_allocate(&GLOBAL_POOL, sizeof(complex));
c3->r = 3.0;
c3->i = 4.0;
printf("Value is (%f + %fi)\n", c3->r, c3->i);
printf("Remaining free size is %ld\n", GLOBAL_POOL->pool_size - GLOBAL_POOL->index);
cur = (mem_block *) &c3;
printf("Position of c3 is %ld\n", cur->pool_position);
printf("Adress of c3's block is %x\n", cur);
printf("Address of c3 is %x\n", &c3);
printf("c3 points to %x\n", c3);
complex *c4 = (complex *) pool_allocate(&GLOBAL_POOL, sizeof(complex));
c4->r = 4.0;
c4->i = 5.0;
printf("Value is (%f + %fi)\n", c4->r, c4->i);
printf("Remaining free size is %ld\n", GLOBAL_POOL->pool_size - GLOBAL_POOL->index);
complex *c5 = (complex *) pool_allocate(&GLOBAL_POOL, sizeof(complex));
c5->r = 5.0;
c5->i = 6.0;
printf("Value is (%f + %fi)\n", c5->r, c5->i);
printf("Remaining free size is %ld\n", GLOBAL_POOL->pool_size - GLOBAL_POOL->index);
complex *c6 = (complex *) pool_allocate(&GLOBAL_POOL, sizeof(complex));
c6->r = 6.0;
c6->i = 7.0;
printf("Value is (%f + %fi)\n", c6->r, c6->i);
printf("Remaining free size is %ld\n", GLOBAL_POOL->pool_size - GLOBAL_POOL->index);
complex *c7 = (complex *) pool_allocate(&GLOBAL_POOL, sizeof(complex));
c7->r = 7.0;
c7->i = 8.0;
printf("Value is (%f + %fi)\n", c7->r, c7->i);
printf("Remaining free size is %ld\n", GLOBAL_POOL->pool_size - GLOBAL_POOL->index);
complex *c8 = (complex *) pool_allocate(&GLOBAL_POOL, sizeof(complex));
if(c8 != NULL) {
c8->r = 3.0;
c8->i = 4.0;
printf("Value is (%f + %fi)\n", c8->r, c8->i);
}else {
return -1;
}
return 0;
}
For now, its size is 128 bytes but once I get the basics, I will make it of unlimited size. Allocation probably works OK, you can see the output and see that I can set the allocated pointers and use the value. On top of this, I wanted to implement free. It is in commented out function pool_free. To perform memmove, I need to know the index of the mem_block I am deleting.
In the pool_allocate, you can see that I return the data pointer of the allocated block, not the whole thing so that it can be used similar to regular malloc. This means, when freeing, I need to recover mem_block from the data pointer. To be able to do this, I put the data pointer as the first element of a mem_block.
Let me explain further on the code
mem_block* cur = (mem_block *) &c3;
printf("Position of c3 is %ld\n", cur->pool_position);
printf("Adress of c3's block is %x\n", cur);
printf("Address of c3 is %x\n", &c3);
printf("c3 points to %x\n", c3);
c3 here is allocated using pool_allocate and resulting pointer to data, uint8_t* in reality was casted to complex * to be used. So c3 points to a complex object. Dereferencing should give the actual data, and I think this works. But it also has its own address. I supposed this address is the same as data pointer of its block since it is the first member in a mem_block. So I casted it freely to a mem_block*. But this does not work.
printf("Position of c3 is %ld\n", cur->pool_position);
says Position of c3 is 0 or other gibberish. I am expecting to see things like 16, 32 etc. since each mem_block is 16 bytes. So, why do you think the cast
mem_block* cur = (mem_block *) &c3;
won't let me work with c3 as if it is a mem_block? Maybe I am failing to correctly pass a pointer by reference so that changes to pointed things are not visible outside? This seems unlikely, since I can work with allocated objects as expected but who knows? I checked every part but still could not solve the issue.
The way this works in malloc is that each block has a header (the management structure) and on alloc you return the address after that structure. Then on free you know you have the address after the header so you can compute the address of the actual header from that.
One simple trick for this is to use
void alloc(size_t size) {
...
struct mem_block *block = <address of block you return>
return &block[1];
}
void free(void *addr) {
struct mem_block *block = addr;
block[-1].is_freed = false;
...
}
I'm pretty sure this will end up being a really obvious question, and that's why I haven't found much information on it. Still, I thought it was worth asking :)
Basically, accessing data using a struct is really fast. If data comes off the network in a form where it can be immediately processed as a struct, this is pretty sweet from a performance point of view.
However, is it possible to define a struct dynamically. Could a client and server app negotiate the format of the datastream and then use that definition as a struct?
If not, is there a better way of doing it?
Thanks all!
It isn't possible to dynamically define a struct that is identical to a compile-time struct.
It is possible, but difficult, to create dynamic structures that can contain the information equivalent to a struct. The access to the data is less convenient than what is available at compile-time.
All else apart, you cannot access a member somestruct.not_seen_at_compile_time using the dot . or arrow -> notation if it was not defined at compile-time.
With network communications, there are other issues to address - notably 'endianness'. That is, the data on the wire will probably include multi-byte (2, 4, 8) integers, and either the MSB or the LSB will be sent first, but if one machine is little-endian (IA-32, IA-64, x86/64) and the other is big-endian (SPARC, PPC, almost anything not from Intel), then the data will need to be transformed. Floating-point formats can also be problematic. There are numerous standards dedicated to defining how data will be sent across the network - it is not trivial. Some are specific: IP, TCP, UDP; others are general, such as ASN.1.
However, the 'cannot do dynamic data structures' part limits things - you have to agree beforehand on what the data structures are, and how they will be interpreted.
How do you do that?
gerty3000 asks:
It is possible, but difficult, to create dynamic structures that can contain the information equivalent to a struct. — How do you do that? I would like to pass dynamically-defined structs off to other C code (assume same compiler and other settings) without having to duplicate the struct memory layout routines from the compiler. I won't be accessing fields of these structs inside my process much (just initializing them once), so convenient syntax is not a concern.
You can't do it without duplicating the memory layout in some shape or form. It might not have to be exactly the same, but it is likely best if it is. Here's some sample code that shows roughly how it might be done.
dynstruct.c
This contains the basic structure manipulation material — structures to describe structures and (simple) members. Handling full arrays (as opposed to strings) would require more work, and there's a good deal of make-work replication to be managed for other types.
It also contains a main() program that tests the code. It makes a call to other_function(), which demonstrates that the structure I've defined in the data structures does match the structure exactly. The data does assume a 64-bit machine where double must be aligned on an 8-byte boundary (so there's a 4-byte hole in the structure); you will have to tweak the data for a machine where double can be on a 4-byte boundary.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* This is the type that will be simulated dynamically */
/*
struct simulated
{
int number;
double value;
char string[32];
};
*/
/* SOF structure.h */
typedef enum Type { INT, DOUBLE, STRING } Type;
typedef struct Descriptor
{
size_t offset;
Type type;
size_t type_size;
size_t array_dim;
char name[32];
} Descriptor;
typedef struct Structure
{
size_t size;
char name[32];
Descriptor *details;
} Structure;
extern void *allocate_structure(const Structure *structure);
extern void deallocate_structure(void *structure);
extern void *pointer_to_element(void *p, const Descriptor *d);
extern int get_int_element(void *p, const Descriptor *d);
extern void set_int_element(void *p, const Descriptor *d, int newval);
extern double get_double_element(void *p, const Descriptor *d);
extern void set_double_element(void *p, const Descriptor *d, double newval);
extern char *get_string_element(void *p, const Descriptor *d);
extern void set_string_element(void *p, const Descriptor *d, char *newval);
/* EOF structure.h */
static Descriptor details[] =
{
{ 0, INT, sizeof(int), 1, "number" },
{ 8, DOUBLE, sizeof(double), 1, "value" },
{ 16, STRING, sizeof(char), 32, "string" },
};
static Structure simulated = { 48, "simulated", details };
void *allocate_structure(const Structure *structure)
{
void *p = calloc(1, structure->size);
return p;
}
void deallocate_structure(void *structure)
{
free(structure);
}
void *pointer_to_element(void *p, const Descriptor *d)
{
void *data = (char *)p + d->offset;
return data;
}
int get_int_element(void *p, const Descriptor *d)
{
assert(d->type == INT);
int *v = pointer_to_element(p, d);
return *v;
}
void set_int_element(void *p, const Descriptor *d, int newval)
{
assert(d->type == INT);
int *v = pointer_to_element(p, d);
*v = newval;
}
double get_double_element(void *p, const Descriptor *d)
{
assert(d->type == DOUBLE);
double *v = pointer_to_element(p, d);
return *v;
}
void set_double_element(void *p, const Descriptor *d, double newval)
{
assert(d->type == DOUBLE);
double *v = pointer_to_element(p, d);
*v = newval;
}
char *get_string_element(void *p, const Descriptor *d)
{
assert(d->type == STRING);
char *v = pointer_to_element(p, d);
return v;
}
void set_string_element(void *p, const Descriptor *d, char *newval)
{
assert(d->type == STRING);
assert(d->array_dim > 1);
size_t len = strlen(newval);
if (len > d->array_dim)
len = d->array_dim - 1;
char *v = pointer_to_element(p, d);
memmove(v, newval, len);
v[len] = '\0';
}
extern void other_function(void *p);
int main(void)
{
void *sp = allocate_structure(&simulated);
if (sp != 0)
{
set_int_element(sp, &simulated.details[0], 37);
set_double_element(sp, &simulated.details[1], 3.14159);
set_string_element(sp, &simulated.details[2], "Absolute nonsense");
printf("Main (before):\n");
printf("Integer: %d\n", get_int_element(sp, &simulated.details[0]));
printf("Double: %f\n", get_double_element(sp, &simulated.details[1]));
printf("String: %s\n", get_string_element(sp, &simulated.details[2]));
other_function(sp);
printf("Main (after):\n");
printf("Integer: %d\n", get_int_element(sp, &simulated.details[0]));
printf("Double: %f\n", get_double_element(sp, &simulated.details[1]));
printf("String: %s\n", get_string_element(sp, &simulated.details[2]));
deallocate_structure(sp);
}
return 0;
}
other.c
This code knows nothing about the structure description material in dynstruct.c; it knows about the struct simulated that the simulation code simulates. It prints the data it is passed and modifies it.
#include <stdio.h>
#include <string.h>
extern void other_function(void *p);
struct simulated
{
int number;
double value;
char string[32];
};
void other_function(void *p)
{
struct simulated *s = (struct simulated *)p;
printf("Other function:\n");
printf("Integer: %d\n", s->number);
printf("Double: %f\n", s->value);
printf("String: %s\n", s->string);
s->number *= 2;
s->value /= 2;
strcpy(s->string, "Codswallop");
}
Sample output
Main (before):
Integer: 37
Double: 3.141590
String: Absolute nonsense
Other function:
Integer: 37
Double: 3.141590
String: Absolute nonsense
Main (after):
Integer: 74
Double: 1.570795
String: Codswallop
Clearly, this code is not production-ready. It is a sufficient demonstration of what can be done. One issue you'd have to deal with is initializing the Structure and Descriptor data correctly. You can't put too many assertions into that sort of code. For example, I should really have assert(d->size == sizeof(double); in get_double_element(). It would also be sensible to include assert(d->offset % sizeof(double) == 0); to ensure that the double element is properly aligned. Or you might have a validate_structure(const Structure *sp); function that did all these validation checks. You'd need a function void dump_structure(FILE *fp, const char *tag, const Structure *sp); to dump the defined structure to the given file preceded by the tag, to assist in debugging. Etc.
This code is pure C; it is not compilable by a C++ compiler as C++. There aren't enough casts to satisfy a C++ compiler.
No, it isn't in C all data types must be known at compile time. That's what makes it "real fast".
Another theoretical possibility would be to compile some code at run-time using a compiler library such as libtcc.
While very appealing in theory (it does sound like a self-modifying application – your application would only have to generate C code for your struct and insert it in a template, then ask libtcc to compile it and then call some functions defined in your template to use that struct), this solution will probably not work great in practice. Why ? Well, as of 2016, libtcc (and the whole tcc project) is not very actively developed and there are issues with architectures such as x86_64.
For dynamic structure, the answer is no.
If you know what data comes in, in C++, you can use the overloaded << in operator to read the data from the stream..
In C, you could convert the stream to a string assuming that you know the length of the data comes in and using the function like sscanf, you could read the data.
You can't define a source-level struct, but you could do the same thing by setting up a data structure to store a name/tag and offset for each field of the data you want to communicate, and then store/read data at the right offsets according to that. Be sure you align all types to a boundary that's a multiple of sizeof(type) for portability. Of course, unless you're sure the client and server will have the same data representations (endianness and other considerations) and really need the performance of direct access, I would write proper serialize and deserialize routines instead...
Based on gerty3000's answer I made a library. I've abstracted something from the final user. It was hard buf finaly worked. If is there any improvement to do I am open for sugestions. Here it goes the code.
type-machine.h // define types and function prototipes
#ifndef TYPE_MACHINE_H
#define TYPE_MACHINE_H
#ifdef __cplusplus
extern "C" {
#endif
#define B8 char
#define B8U unsigned char
#define B16 short
#define B16U unsigned short
#define B32 int
#define B32U unsigned int
#define B64 long long int
#define B64U unsigned long long int
#define BP32 float
#define BP64 double
#define BIT_ON(var,bit) ((var)=((var) | (bit)))
#define BIT_OFF(var,bit) ((var)=((var) & (~bit)))
#define BIT_IS_ON(var,bit) (var & bit)
#define PAIR(position,value) ((value)=((position) << (1)))
typedef struct Bit8Tag BIT;
typedef enum {
Off, On
} STATUS;
typedef enum {
B8_T, B8U_T, B16_T, B16U_T, B32_T, B64_T, B64U_T, B32U_T, BP32_T, BP64_T
} TYPE;
typedef struct ClassFieldTag ClassField;
typedef struct ClassTag Class;
typedef enum {
CLASS_SIZE, CLASS_INSERT, CLASS_SHOW
} CLASS_MODE;
#if (defined(WIN32) || defined(WINDOWS_XP))
#define is_win()(1)
#else
#define is_win()(0)
#define TYPE_CALL
#define TYPE_TYPE
#endif // WIN32
#include <math.h>
#include <string.h>
#include <assert.h>
#define area(a,b) ((a)*(b))
#define radian(x,y)(atan2(y,x))
#define angle(a)( (a * (180 / M_PI)) + 180)
#if defined WIN32
#define ARIAL_PATH "C:/Windows/Fonts/arial.ttf\0"
#else
#define ARIAL_PATH "home/media/TheGreat/\0"
#endif
struct ClassFieldTag {
TYPE type;
size_t mem, size, len;
B8 name[32];
struct ClassFieldTag * next, *preview;
};
extern ClassField * class_set_push();
extern ClassField * class_field_set(ClassField * set, TYPE type, B8 * name, size_t len, size_t mem);
extern STATUS class_set_next_back(ClassField ** set, ClassField * next);
extern STATUS class_set_next_front(ClassField ** set, ClassField * next);
extern STATUS class_insert_back(Class * set, TYPE type, B8 * name, size_t len);
extern STATUS class_insert_front(Class * set, TYPE type, B8 * name, size_t len);
struct ClassTag {
B8 name[32];
void * data;
B8 * String;
B16 Short;
B16U UShort;
B32 Int;
B32U UInt;
B64 Long;
B64 ULong;
BP32 Float;
BP64 Double;
ClassField * field;
};
Class * class_push(B8 name[32]);
extern STATUS class_zero(Class * set, B8 name[32]);
extern void class_data_push(Class * set);
extern void class_data_pop(Class * set);
extern void * class_set_to(Class * set, ClassField * field);
extern void class_int_set(Class * set, ClassField * field, B32 value);
extern B32 class_int_get(Class * set, ClassField * field);
extern void class_double_set(Class * set, ClassField * field, BP64 value);
extern BP64 class_double_get(Class * set, ClassField * field);
extern void class_string_set(Class * set, ClassField * field, B8 * value);
extern B8 * class_string_get(Class * set, ClassField * field);
extern void class_mode(Class * set, ClassField * field, CLASS_MODE mode);
extern void class_field_pop(Class * set);
extern void class_pop(Class * set);
extern STATUS class_ex(Class * mine);
struct Bit8Tag {
unsigned b16 : 16;
};
extern void bit_on(BIT * value, int bit);
extern void bit_off(BIT * value, int bit);
extern STATUS bit_is_on(BIT value, int bit);
extern B32U strsub(B8 * data, B8 * key);
#ifdef __cplusplus
}
#endif
#endif // TYPE_MACHINE_H
type-machine.c // declares those functions
#include <Place/include/type-machine.h>
#include <malloc.h>
#include <stdio.h>
Class * class_push(B8 name[32]) {
Class * set = (Class *) malloc(sizeof (Class));
if(class_zero(set,name)){
return(set);
}
return(NULL);
}
void class_data_push(Class * set) {
B32 class_size = sizeof (Class), class_field_size = sizeof (ClassField);
if (set) {
if (class_size < sizeof (set))class_size = sizeof (set);
if (class_field_size < sizeof (set->field))class_field_size = sizeof (set->field);
}
set->data = malloc(class_size + class_field_size + 1);
}
void class_data_pop(Class * set) {
if (set && set->data) {
free(set->data);
}
}
void * class_set_to(Class * set, ClassField * field) {
if (set && set->data && field) {
void * data = (char *) set->data + field->mem;
return data;
}
return (NULL);
}
void class_int_set(Class * set, ClassField * field, B32 value) {
if (set) {
assert(field->type == B32_T);
B32 * update = class_set_to(set, field);
*update = value;
}
}
B32 class_int_get(Class * set, ClassField * field) {
if (set) {
assert(field->type == B32_T);
B32 * data = class_set_to(set, field);
return (*data);
}
return (0);
}
void class_double_set(Class * set, ClassField * field, BP64 value) {
if (set) {
assert(field->type == BP64_T);
BP64 * update = class_set_to(set, field);
*update = value;
}
}
BP64 class_double_get(Class * set, ClassField * field) {
if (set) {
assert(field->type == BP64_T);
BP64 * data = class_set_to(set, field);
return (*data);
}
return (0);
}
void class_string_set(Class * set, ClassField * field, B8 * value) {
if (set && field && field->len > 1 && value) {
assert(field->type == B8_T);
size_t len = strlen(value);
if (len < 2) {
len = 2;
}
if (len > field->len)len = field->len - 1;
B8 * buffer = class_set_to(set, field);
if (buffer) {
memmove(buffer, value, len);
buffer[len] = '\0';
}
}
}
B8 * class_string_get(Class * set, ClassField * field) {
if (set && field) {
assert(field->type == B8_T);
B8 * data = class_set_to(set, field);
return (data);
}
return (NULL);
}
STATUS class_zero(Class * set, B8 * name) {
if (set) {
set->String = NULL;
set->Short = 0;
set->UShort = 0;
set->Int = 0;
set->UInt = 0;
set->Long = 0;
set->ULong = 0;
set->Float = 0;
set->Double = 0;
set->data = NULL;
memset(set->name, 0, sizeof (set->name));
if (name)memmove(set->name, name, strlen(name));
set->field = NULL;
return (On);
}
return (Off);
}
ClassField * class_set_push() {
return (malloc(sizeof (ClassField)));
}
void class_field_pop(Class * set) {
if (set) {
ClassField * field = set->field;
while (field) {
ClassField * next = field->next;
if (field) {
free(field);
field = NULL;
}
field = next;
}
}
}
void class_pop(Class * set) {
if (set) {
class_data_pop(set);
class_field_pop(set);
free(set);
set = NULL;
}
}
ClassField * class_field_set(ClassField * field, TYPE type, B8 * name, size_t len, size_t mem) {
if (field) {
size_t lenght = (name) ? strlen(name) : 0;
if (lenght > 32) {
lenght = 31;
}
memcpy(field->name, name, lenght);
field->name[lenght] = 0;
field->type = type;
field->mem = mem;
field->len = len;
class_mode(NULL, field, CLASS_SIZE);
field->next = NULL;
field->preview = NULL;
return (field);
}
return (NULL);
}
STATUS class_set_next_back(ClassField ** field, ClassField * next) {
if (next == NULL)return (Off);
next->next = *field;
if (*field != NULL) {
(*field)->preview = next;
}
*field = next;
return (On);
}
STATUS class_set_next_front(ClassField ** field, ClassField * next) {
if (next == NULL)return (Off);
if (*field != NULL) {
ClassField * update = *field, *preview = NULL;
while (update->next != NULL) {
preview = update;
update = update->next;
}
update->preview = preview;
update->next = next;
return (On);
}
*field = next;
return (On);
}
STATUS class_insert_back(Class * set, TYPE type, B8 * name, size_t len) {
if (class_set_next_back(&set->field, class_field_set(class_set_push(), type, name, len, 0))) {
ClassField * preview = set->field;
if (preview->next) {
preview->mem = preview->next->mem + preview->next->size;
}
return (On);
}
return (Off);
}
STATUS class_insert_front(Class * set, TYPE type, B8 * name, size_t len) {
ClassField * next = class_field_set(class_set_push(), type, name, len, 0);
if (class_set_next_front(&set->field, next)) {
ClassField * preview = set->field;
while (preview) {
if (preview->next) {
if (preview->next == next) {
next->mem = preview->mem + preview->size;
}
}
preview = preview->next;
}
return (On);
}
return (Off);
}
void class_mode(Class * set, ClassField * field, CLASS_MODE mode) {
if (field) {
switch (field->type) {
case B8_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: %s\n", field->name, class_string_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = field->len * sizeof (B8);
}
break;
case CLASS_INSERT:
{
class_string_set(set, field, set->String);
}
break;
}
}
break;
case B8U_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: %s\n", field->name, class_string_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = field->len * sizeof (B8U);
}
break;
case CLASS_INSERT:
{
class_string_set(set, field, set->String);
}
break;
}
}
break;
case B16_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%i]\n", field->name, class_int_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (B16);
}
break;
case CLASS_INSERT:
{
class_int_set(set, field, set->Int);
}
break;
}
}
break;
case B16U_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%i]\n", field->name, class_int_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (B16U);
}
break;
case CLASS_INSERT:
{
class_int_set(set, field, set->Int);
}
break;
}
}
break;
case B32_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: %i\n", field->name, class_int_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (B32);
}
break;
case CLASS_INSERT:
{
class_int_set(set, field, set->Int);
}
break;
}
}
break;
case B32U_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%i]\n", field->name, class_int_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (B32U);
}
break;
case CLASS_INSERT:
{
class_int_set(set, field, set->Int);
}
break;
}
}
break;
case B64_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%i]\n", field->name, class_int_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (B64);
}
break;
case CLASS_INSERT:
{
class_int_set(set, field, set->Int);
}
break;
}
}
break;
case B64U_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%i]\n", field->name, class_int_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (B64U);
}
break;
case CLASS_INSERT:
{
class_int_set(set, field, set->Int);
}
break;
}
}
break;
case BP32_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%lf]\n", field->name, class_double_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (BP32);
}
break;
case CLASS_INSERT:
{
class_double_set(set, field, set->Double);
}
break;
}
}
break;
case BP64_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%lf]\n", field->name, class_double_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (BP64);
}
break;
case CLASS_INSERT:
{
class_double_set(set, field, set->Double);
}
break;
}
}
break;
}
}
}
void bit_on(BIT * value, int bit) {
BIT_ON(value->b16, bit);
}
void bit_off(BIT * value, int bit) {
BIT_OFF(value->b16, bit);
}
STATUS bit_is_on(BIT value, int bit) {
if (value.b16 & bit)return (On);
return (Off);
}
B32U strsub(B8 * data, B8 * key) {
if (data && key) {
B8 *d = data;
B32U len = strlen(key), p = 0;
if (len > strlen(d))return (0);
while (*d != '\0') {
if (*(d + len) != '\0') {
B32U x = 0;
while (x <= len) {
if (key[x] == *d) {
*d++;
p++;
} else break;
x++;
}
if (x == len)return (p);
} else if (len == 1) {
if (*d == key[0])return (p);
}
p++;
*d++;
}
}
return (0);
}
main.c // testing....
#include "network.h"
#include <conio.h>
STATUS class_ex(Class * set) {
class_data_push(set);
if (set->data) {
ClassField * field = set->field;
while (field) {
if (!strcmp(field->name, "peso")) {
set->Double = 65.5;
}
if (!strcmp(field->name, "idade")) {
set->Int = 29;
}
if (!strcmp(field->name, "nome")) {
set->String = "Lisias de Castro Martins";
}
if (!strcmp(field->name, "endereco")) {
set->String = "Rua Mae D'Agua";
}
class_mode(set, field, CLASS_INSERT);
class_mode(set, field, CLASS_SHOW);
field = field->next;
}
return (On);
}
return (Off);
}
int main(int argc, char** argv) {
STATUS client_start = On;
if (client_start) {
Class * client = class_push("Client");;
class_insert_back(client, BP64_T, "peso", 1);
class_insert_back(client, B8_T, "endereco", 32);
class_insert_back(client, B32_T, "idade", 1);
class_insert_back(client, B8_T, "nome", 64);
printf("Classe[%s]\n\n", client->name);
if (class_ex(client)) {
}
class_pop(client);
getch();
}
return (EXIT_SUCCESS);
}
I still have to implement the short double and some other functions, but it is working.