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 :)
B
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...
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.
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.
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.
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;
}
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");
}
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".
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.