Dealing with nested struct access - c

What is the best way to avoid writing something like:
someDataStruct.longSubStructName.anotherLongName.theVariable =
someStruct.longSubStructName.anotherLongName.theVariable + 10;
Setters and getters would be a work around in oop programs. But what would be the best way to deal with this in for example C without having any objects?

Create a pointer to the variables in question:
int *someDataVar1 = &someDataStruct.longSubStructName.anotherLongName.theVariable;
int *someDataVar2 = &someStruct.longSubStructName.anotherLongName.theVariable;
*someDataVar1 = *someDataVar2 + 10;
If you're using a deeply nested field like this many times in a block of code, doing something like this can help to increase readability. If you only need to use it once or twice however you're probably better off leaving it way it is.
Here's an example where such a construct might make sense:
for (clientNum = 0; clientNum < clientCount; clientNum++) {
printf("name: %s\n", mainStructure.subStructure.clientList[clientNum].name);
printf("address: %s\n", mainStructure.subStructure.clientList[clientNum].address);
printf("identifier: %x\n", mainStructure.subStructure.clientList[clientNum].identifier);
printf("file count: %d\n", mainStructure.subStructure.clientList[clientNum].fileCount);
for (fileNum = 0; fileNum < mainStructure.subStructure.clientList[clientNum].fileCount; fileNum++) {
printf("filename: %s\n", mainStructure.subStructure.clientList[clientNum].fileList[fileNum].fileName);
printf("size: %d\n", mainStructure.subStructure.clientList[clientNum].fileList[fileNum].size);
printf("checksum: %d\n", mainStructure.subStructure.clientList[clientNum].fileList[fileNum].checksum);
mainStructure.subStructure.clientList[clientNum].fileList[fileNum].printed++;
}
mainStructure.subStructure.clientList[clientNum].printed++;
}
Lots of verbose, repeated information here. So much so it can make it difficult to see what you're actually working with. This can be made more readable as follows:
for (clientNum = 0; clientNum < clientCount; clientNum++) {
struct client *thisClient = &mainStructure.subStructure.clientList[clientNum];
printf("name: %s\n", thisClient->name);
printf("address: %s\n", thisClient->address);
printf("identifier: %x\n", thisClient->identifier);
printf("file count: %d\n", thisClient->fileCount);
for (fileNum = 0; fileNum < thisClient->fileCount; fileNum++) {
struct file *thisFile = &thisClient.fileList[fileNum];
printf("filename: %s\n", thisFile->fileName);
printf("size: %d\n", thisFile->size);
printf("checksum: %d\n", thisFile->checksum);
thisFile->printed++;
}
thisClient->printed++;
}

If you can make your inner structs anonymous typedefs, you can use plan9 extensions to access the sub-struct's member directly (assuming there aren't any variable name conflicts). Disclaimer: I do not really recommend this method as I prefer standard-conforming C, but figure I'd lob it out there because it does exist. Here's an example of it:
#include <stdlib.h>
#include <stdio.h>
typedef struct{
int x;
int y;
} inner_struct1;
typedef struct{
int v;
int w;
} inner_struct2;
struct outer_struct {
inner_struct1;
inner_struct2;
int a;
int b;
int c;
};
int main(void)
{
struct outer_struct os;
os.x = 1;
os.y = 2;
os.v = 3;
os.w = 4;
os.a = 5;
os.b = 6;
os.c = 7;
printf("inner_struct1.x: %d, inner_struct1.y: %d, inner_struct2.v: %d,
inner_struct2.w: %d os.a: %d, os.b: %d, os.c: %d\n",
os.x, os.y, os.v, os.w, os.a, os.b, os.c);
}
With gcc, you'd have to compile with either -fplan9-extensions or -fms-extensions.
clang does not have plan9, so only -fms-extensions will work.

Transfer these operation inside a function with meaning full name and use inline.
inline void someFunctionNameAdd(int val)
{
someDataStruct.longSubStructName.anotherLongName.theVariable =
someStruct.longSubStructName.anotherLongName.theVariable + val;
}
then call function someFunctionName(10);
Other way use define macro:
#define SOME_MEANINGFULLNAME \
someDataStruct.longSubStructName.anotherLongName.theVariable
then:
SOME_MEANINGFULLNAME+=10;

Related

Dynamically allocate and initialize new object with 30% probability

I'm writing a program that will simulate a randomized race between runners who are climbing up a mountain where dwarf orcs (dorcs) are coming down the mountain to attack the runners. It begins with two runners named harold and timmy at the bottom of the mountain. The runners make their way up the mountain in randomized moves where they may make progress forward up the mountain, or they may slide back down the mountain. Dorcs are randomly generated, and they inflict damage on a runner if they collide. The simulation ends when one of the runners reaches the top of the mountain, or when both runners are dead.
I'm struggling with a part where I have to implement the actual race loop. Once the race is initialized, the race loop will iterate until the race is over. This happens when either a winner has been declared, or when all runners are dead.
Every iteration of the race loop will do the following:
with 30% probability, dynamically allocate a new dorc as an EntityType structure, and initialize it as follows:
(a) a dorc’s avatar is always “d”
(b) each dorc begins the race at the top of the mountain, which is at row 2
(c) with equal probability, the dorc may be placed either in the same column as timmy, or in the same column as the harold, or in the column exactly half-way between the two
(d) add the new dorc to the race’s array of dorcs
(e) using the pthread_create() function, create a thread for the new dorc, and save the thread pointer in the dorc’s entity structure; the function that each dorc thread will execute is the void* goDorc(void*) function that you will implement in a later step; the parameter to the goDorc() function will be the EntityType pointer that corresponds to that dorc
I guess I'm confused with the logic of how to approach this. I decided to make a function called isOver() to indicate if the race is over, and then a separate function called addDorc() to initialize the Dorc elements and do all the requirements above.
In isOver(), I attempt to add a dorc object to the dorcs array by doing addDorc(race); with every iteration of the race loop/if the race hasn't ended or no one died. But I keep getting the error:
control.c:82:3: error: too few arguments to function ‘addDorc’
addDorc(race);
The problem is I don't think I can manually declare all the parameters in addDorc() because some elements like the "path" argument are based on probability. As mentioned above, with equal probability, the dorc may be placed either in the same column as timmy, or in the same column as the harold, or in the column exactly half-way between the two. The issue is I don't know how to factor this random value when calling addDorc() and would appreciate some help. I also don't know if I'm doing the "with 30% probability, dynamically allocate a new dorc as an EntityType structure" correctly and would be grateful for some input on that as well.
defs.h
typedef struct {
pthread_t thr;
char avatar[MAX_STR];
int currPos;
int path;
} EntityType;
typedef struct {
EntityType ent;
char name[MAX_STR];
int health;
int dead;
} RunnerType;
typedef struct {
int numRunners;
RunnerType *runners[MAX_RUNNERS];
int numDorcs;
EntityType *dorcs[MAX_DORCS];
char winner[MAX_STR];
int statusRow;
sem_t mutex;
} RaceInfoType;
void launch();
int addDorc(RaceInfoType*, char*, int, int);
int isOver(RaceInfoType*);
void initRunners(RaceInfoType*);
int addRunner(RaceInfoType*, char*, char*, int, int, int, int);
int randm(int);
void *goRunner(void*);
void *goDorc(void*);
RaceInfoType *race;
control.c
void launch(){
race = malloc(sizeof(RaceInfoType));
race->numRunners = 0;
initRunners(race);
if (sem_init(&race->mutex, 0, 1) < 0) {
printf("semaphore initialization error\n");
exit(1);
}
strcpy(race->winner, " ");
srand((unsigned)time(NULL));
int i;
for(i = 0; i < race->numRunners; ++i){
pthread_create(&(race->runners[i]->ent.thr), NULL, goRunner, " ");
}
race->numDorcs = 0;
}
int addDorc(RaceInfoType* race, char *avatar, int path, int currPos){
if(race->numDorcs == MAX_DORCS){
printf("Error: Maximum dorcs already reached. \n");
return 0;
}
race->dorcs[race->numDorcs] = malloc(sizeof(EntityType));
int timmysColumn = race->dorcs[race->numDorcs]->currPos;
int haroldsColumn = race->dorcs[race->numDorcs]->currPos;
int halfwayColumn = (timmysColumn+haroldsColumn)/2;
int r = rand()%100;
pthread_t dorc;
if(r <= 30){
strcpy(race->dorcs[race->numDorcs]->avatar, "d");
race->dorcs[race->numDorcs]->currPos = 2;
if(r <= 33){
race->dorcs[race->numDorcs]->path = timmysColumn;
}else if(r <= 66){
race->dorcs[race->numDorcs]->path = haroldsColumn;
}else{
race->dorcs[race->numDorcs]->path = halfwayColumn;
}
pthread_create(&dorc, NULL, goDorc, " ");
}
race->numRunners++;
}
int isOver(RaceInfoType* race){
int i;
for(i = 0; i < race->numRunners; ++i){
if((race->winner != " ") || (race->runners[race->numRunners]->dead = 1)){
return 1;
}
addDorc(race);
return 0;
}
}
void initRunners(RaceInfoType* r){
addRunner(r, "Timmy", "T", 10, 35, 50, 0);
addRunner(r, "Harold", "H", 14, 35, 50, 0);
}
int addRunner(RaceInfoType* race, char *name, char *avatar, int path, int currPos, int health, int dead){
if(race->numRunners == MAX_RUNNERS){
printf("Error: Maximum runners already reached. \n");
return 0;
}
race->runners[race->numRunners] = malloc(sizeof(RunnerType));
strcpy(race->runners[race->numRunners]->name, name);
strcpy(race->runners[race->numRunners]->ent.avatar, avatar);
race->runners[race->numRunners]->ent.path = path;
race->runners[race->numRunners]->ent.currPos = currPos;
race->runners[race->numRunners]->health = health;
race->runners[race->numRunners]->dead = dead;
race->numRunners++;
return 1;
}
Caveat: Because there's so much missing [unwritten] code, this isn't a complete solution.
But, I notice at least two bugs: the isOver bugs in my top comments. And, incrementing race->numRunners in addDorc.
isOver also has the return 0; misplaced [inside the loop]. That should go as the last statement in the function. If you had compiled with -Wall [which you should always do], that should have been flagged by the compiler (e.g. control reaches end of non-void function)
From that, only one "dorc" would get created (for the first eligible runner). That may be what you want, but [AFAICT] you want to try to create more dorcs (one more for each valid runner).
Also, the bug the compiler flagged is because you're calling addDorc(race); but addDorc takes more arguments.
It's very difficult to follow the code when you're doing (e.g.) race->dorcs[race->numDorcs]->whatever everywhere.
Better to do (e.g.):
EntityType *ent = &race->dorcs[race->numDorcs];
ent->whatever = ...;
Further, it's likely that your thread functions would like a pointer to their [respective] control structs (vs. just passing " ").
Anyway, I've refactored your code to incorporate these changes. I've only tried to fix the obvious/glaring bugs from simple code inspection, but I've not tried to recompile or address the correctness of your logic.
So, there's still more work to do, but the simplifications may help a bit.
void
launch(void)
{
race = malloc(sizeof(RaceInfoType));
race->numRunners = 0;
initRunners(race);
if (sem_init(&race->mutex,0,1) < 0) {
printf("semaphore initialization error\n");
exit(1);
}
strcpy(race->winner," ");
srand((unsigned)time(NULL));
int i;
for (i = 0; i < race->numRunners; ++i) {
RunnerType *run = &race->runners[i];
EntityType *ent = &run->ent;
pthread_create(&ent->thr,NULL,goRunner,ent);
}
race->numDorcs = 0;
}
int
addDorc(RaceInfoType* race,char *avatar,int path,int currPos)
{
if (race->numDorcs == MAX_DORCS) {
printf("Error: Maximum dorcs already reached. \n");
return 0;
}
EntityType *ent = malloc(sizeof(*ent));
race->dorcs[race->numDorcs] = ent;
int timmysColumn = ent->currPos;
int haroldsColumn = ent->currPos;
int halfwayColumn = (timmysColumn + haroldsColumn) / 2;
int r = rand()%100;
#if 0
pthread_t dorc;
#endif
if (r <= 30) {
strcpy(ent->avatar,"d");
ent->currPos = 2;
if (r <= 33) {
ent->path = timmysColumn;
} else if (r <= 66) {
ent->path = haroldsColumn;
} else {
ent->path = halfwayColumn;
}
pthread_create(&ent->thr,NULL,goDorc,ent);
}
#if 0
race->numRunners++;
#else
race->numDorcs += 1;
#endif
}
int
isOver(RaceInfoType* race)
{
int i;
for (i = 0; i < race->numRunners; ++i) {
#if 0
if ((race->winner != " ") ||
(race->runners[race->numRunners]->dead = 1))
return 1;
#else
RunnerType *run = &race->runners[i];
if ((race->winner != " ") || (run->dead == 1))
return 1;
#endif
addDorc(race);
#if 0
return 0;
#endif
}
#if 1
return 0;
#endif
}
void
initRunners(RaceInfoType* r)
{
addRunner(r,"Timmy","T",10,35,50,0);
addRunner(r,"Harold","H",14,35,50,0);
}
int
addRunner(RaceInfoType* race,char *name,char *avatar,int path,int currPos,
int health,int dead)
{
if (race->numRunners == MAX_RUNNERS) {
printf("Error: Maximum runners already reached. \n");
return 0;
}
RunnerType *run = malloc(sizeof(*run));
race->runners[race->numRunners] = run;
strcpy(run->name,name);
EntityType *ent = &run->ent;
strcpy(ent->avatar,avatar);
ent->path = path;
ent->currPos = currPos;
run->health = health;
run->dead = dead;
race->numRunners++;
return 1;
}
UPDATE:
I noticed in addDorc(), you put pthread_t dorc; in an if statement. I don't quite understand what my if statement is actually supposed to be checking though.
I forgot to mention/explain. I wrapped your/old code and my/new code with preprocessor conditionals (e.g.):
#if 0
// old code
#else
// new code
#endif
After the cpp stage, the compiler will only see the // new code stuff. Doing this was an instructional tool to show [where possible] what code you had vs what I replaced it with. This was done to show the changes vs. just rewriting completely.
If we never defined NEVERWAS with a #define NEVERWAS, then the above block would be equivalent to:
#ifdef NEVERWAS
// old code ...
#else
// new code
#endif
Would it still be under the if(r <= 30) part like I did in my original code?
Yes, hopefully now, it is more clear. #if is a cpp directive to include/exclude code (as if you had edited that way). But, a "real" if is an actual executable statement that is evaluated at runtime [as it was before], so no change needed.
My other concern is it doesn't look like dorc is used anywhere in the function because you write pthread_create(&ent->thr,NULL,goDorc,ent); which seems to use ent instead?
That is correct. It is not used/defined and the value goes to ent->thr. As you had it, the pthread_t value set by pthread_create would be lost [when dorc goes out of scope]. So, unless it's saved somewhere semi-permanent (e.g. in ent->thr), there would be no way to do a pthread_join call later.

C Realloc Error - Dynamic Array in struct

first of all thank you so much for trying to help me, or at least have a look at my problem and maybe tell me, what I am doing wrong.
Ok, so I've recently encountered the problem that realloc() throws a segmentation fault error, when used to extend an array in a typedef struct, or at least so it does in my case.
I've tried to find the solution online, on this webpage and on other ones, but sadly I couldn't seem to find one. Which is why I really hope that one of you would be so kind to help me.
Thank you so much in advance,
Matthias
Btw. here I appended the code that is providing me errors.
typedef struct dict_int {
int* size;
char** keys;
int* values;
} dict_int;
This is my struct. And below is the function (add_int()), which is making problems... ;(
void add_int(dict_int* dictionary, int len, ...) {
va_list valist;
va_start(valist, len);
int di_size = dictionary->size[0];
for(int i = di_size-1; i < di_size-1+len; i++) {
dictionary->keys[i] = va_arg(valist, char*);
dictionary->values[i] = va_arg(valist, int);
printf("Change [Size]: %d -> ", dictionary->size[0]);
dictionary->size[0]++;
printf("%d\n", dictionary->size[0]);
if((dictionary->keys = (char**) realloc(dictionary->keys, dictionary->size[0] * 4 + 4))
!= NULL) {
printf("Worked out!");
} else
printf("Didn't work!");
if((dictionary->values = (int*) realloc(dictionary->values, dictionary->size[0] * 4 + 4))
!= NULL) {
printf("Worked out!");
} else
printf("Didn't work!");
}
va_end(valist);
}
And here's how I used it in the main() function:
int main(int argc, char* argv[]) {
printf("\n - !Program has started! - \n");
dict_int dictionary = Dict_int(2, "first", 0, "second", 1);
printf("\nDictionary-Size: %d\n", dictionary.size[0]);
printf("Dictionary: %s: %d\n", dictionary.keys[0], dictionary.values[0]);
printf("Dictionary: %s: %d\n", dictionary.keys[1], dictionary.values[1]);
printf("Dictionary: first: %d\n", get_int(&dictionary, "first"));
add_int(&dictionary, 1, "third", 2);
add_int(&dictionary, 1, "uno", 1);
/* printf("uno: %d\n", get_int(&dictionary, "uno"));
printf("third: %d\n", get_int(&dictionary, "third"));
printf("\n - !Program has finished! - \n");*/
return 0;
}
Also, the code works out alright, if I only call add_int() once, so if I only were to add one element to my dict_int it wouldn't throw an error, which is why I'm guessing that it has something to do with the memory...
Also, I tried to remove all the pieces of add_int() that included a realloc(), in order to prove my theory, and without realloc() it seems to work just fine...
So, just know that I'm very grateful to you, for just reading trough this question, and I'd be even more grateful, if you were so kind, and tried to tell me what I'm doing wrong, since sadly, I can't figure out. - Matthias

How to make a function return a pointer to array struct

I'm working on a function in C that should return a pointer to an array struct.
The struct is
struct corr{
char nome[128];
char cognome[128];
char password[10];
float importo;
};
typedef struct corr correntista;
The function that return a pointer to this struct is
correntista *max_prelievo(FILE *fcorrentisti, FILE *fprelievi){
correntista *corr_max[2];
corr_max[0] = (correntista *)malloc(sizeof(correntista));
corr_max[1] = (correntista *)malloc(sizeof(correntista));
/*.........*/
return *corr_max;
}
In the main program I want to print the returned value in the following way:
*c_max = max_prelievo(fc, fp);
printf("Correntista con max prelievi:\n");
printf("Nome: %s\n", c_max[0]->nome);
printf("Cognome: %s\n", c_max[0]->cognome);
printf("Max prelievo: %f\n\n", c_max[0]->importo);
printf("Correntista con max versamenti:\n");
printf("Nome: %s\n", c_max[1]->nome);
printf("Cognome: %s\n", c_max[1]->cognome);
printf("Max versamento: %f\n", c_max[1]->importo);
but only the first struct c_max[0] has the expected value. c_max[1] has garbage values. What should I change in my program?
There are several solutions, but they all require an overhaul of how your function is to operate. Perhaps the best solution is to consider how scanf works; It returns no objects, instead it operates solely on objects that are pointed to by parameters passed to it. Hence:
void max_prelievo(correntista *corr_max, FILE *fcorrentisti, FILE *fprelievi){
/* NOTE: You should make a habit of separating your allocation from this logic;
* it makes debugging and testing so much easier... */
corr_max[0] = (correntista) { .nome = "Michael",
.cognome = "Anderson",
.importo = 42.0 };
corr_max[1] = (correntista) { .nome = "Undefined",
.cognome = "Behaviour",
.importo = -1.0 };
/*.........*/
}
int main(void) {
correntistia c_max[2] = { 0 };
FILE *f1 = fopen(...), *f2 = fopen(...);
max_prelievo(c_max, f1, f2);
printf("Correntista con max prelievi:\n");
printf("Nome: %s\n", c_max[0].nome);
printf("Cognome: %s\n", c_max[0].cognome);
printf("Max prelievo: %f\n\n", c_max[0].importo);
printf("Correntista con max versamenti:\n");
printf("Nome: %s\n", c_max[1].nome);
printf("Cognome: %s\n", c_max[1].cognome);
printf("Max versamento: %f\n", c_max[1].importo);
}
What scanf does return is a single integer, indicating success. Perhaps, if you are to use return values in the future, you could resort to using a similar pattern? Look at the bright side: There's no malloc or free here and no chance you could leak any memory; the code is as simple as it could possibly get.
Alternatively, as horrific as it seems, if you must resort to using such a pattern then less calls to malloc will serve you well. Consider something like this:
correntista *max_prelievo(FILE *fcorrentisti, FILE *fprelievi) {
correntista *corr_max = malloc(2 * sizeof *corr_max);
corr_max[0] = (correntista) { .nome = "Michael",
.cognome = "Anderson",
.importo = 42.0 };
corr_max[1] = (correntista) { .nome = "Undefined",
.cognome = "Behaviour",
.importo = -1.0 };
/*.........*/
return corr_max;
}
The main entry point will be almost identical to the example I gave earlier... except for one thing: Don't forget to free that memory! :(
Remember what you have is array of pointers so when you exit the function the array is out of scope. Have a pointer to pointer for this purpose as shown below.
correntista **max_prelievo(FILE *fcorrentisti, FILE *fprelievi){
correntista **corr_max = malloc(sizeof(correntista *) * 2);
corr_max[0] = malloc(sizeof(correntista));
corr_max[1] = malloc(sizeof(correntista));
/*.........*/
return corr_max;
}
The call should be
correntista **c_max = max_prelievo(fc, fp);

Initialize int array in struct in C

I have written a type:
typedef struct
{
int Tape[TAPE_SIZE];
int *Head;
int Tape_Count;
int Loop_Start_Count;
} Tarpit;
I try to initialize this type with the following function:
void Tarpit_Initialize(Tarpit Tarpit)
{
Tarpit.Tape_Count = 0;
Tarpit.Loop_Start_Count = 0;
int Index;
for(Index = 0; Index < TAPE_SIZE; Index++)
{
Tarpit.Tape[Index] = INITIAL_SIZE;
}
}
However, it does not seem to work. If I run this:
Tarpit Foo;
Tarpit_Initialize(Foo);
printf("Tarpit Initialization Test: \n");
int index;
for(index = 0; index < TAPE_SIZE ; index++)
{
if(Foo.Tape[index] == INITIAL_SIZE)
{
printf("%d: %d \n", index, Foo.Tape[index]);
}
else
{
printf("%d: %d !ERROR \n", index, Foo.Tape[index]);
}
}
I get several non-zero values (I have set #define TAPE_SIZE 10000 and #define INITIAL_SIZE 0)
Moreover, if I run the test without running Tarpit_Initialize(Foo), I get exactly the same results. The initializer does not seem to work. Why/how could I implement it in an other way? I would like to set every element of Foo.Tape to zero.
Problem solved!
You are passing Tarpit by value:
void Tape_Initialize(Tarpit Tarpit)
That means it is only a copy of Tarpit. You have to pass a pointer to it to be able to modify it.
void Tape_Initialize(Tarpit* Tarpit)
and pass it as pointer (note the name of the function called!):
Tape_Initialize(&Foo);
and the use the -> operator to modify it. For instance:
Tarpit->Tape_Count = 0;
Moreover, as "Elias Van Ootegem" pointed out, you should not use sizeof(Tarpit.Tape) to get the size of the array but TAPE_LENGTH that you defined. Because sizeof() will give you a size in bytes not in elements.
Have you checked the function u are calling ??
Its "Tarpit_Initialize(Foo);"
But the Function u are using it to initialize "void Tape_Initialize(Tarpit Tarpit)".
I think even what u have implemented should work fine .

printf with reference arguments

Imagine having a label that after created updates x amount of times / sec. The text of the label is given as a format-specifier text (ala printf), and any arguments for the format-specifier is updated on redraw, because the arguments for the format specifier is pointers to their respective values.
Does any variant of sprintf work like this?
The code would work something like this:
/* client */
createLabel("Value is %f", &myFloatValue);
I haven't quite figured out a way to do this yet, does anyone have any ideas? I guess one could parse the format text, retrieve the pointers (and types), and store them as some object in a list, where you later could reprint the text and maybe delegate the formatting to the objects themselves, passing them only a textbuffer.. hmmm
Btw, the interface is C, but the host is C++.
Okay i got a "working" prototype, but it's written mainly in assembler. Anyway it demonstrates the supposed use of the api. Can anyone see a portable way to do this / have a better idea for the implementation?
It's pretty large so i'm posting it on pastebin:
http://pastebin.com/H8ZpWb4u
So your createLabel interface would store the format string, along with the addresses of the variables you're wanting to display within the string. Then just use standard old sprintf to reformat the text. Just be careful with those pointers to the data, and make sure you invalidate them when necessary.
I'm not really sure what the problem is. What else are you looking for? sprintf is capable of doing what you want, but you're going to have to track the format string and variable addresses yourself.
Okay i suddenly got an idea .. stringstream + templated polymorphism. I ended up writing the thing in C++ in 5 mins, and at the very least it's a huge improvement.
#include <string>
#include <iostream>
#include <vector>
#include <sstream>
class CBaseValue
{
public:
virtual void toString(std::stringstream & buf) = 0;
};
template< typename T >
class CValue : public CBaseValue
{
typedef T type;
typedef T * ptr_type;
type * val;
public:
CValue(void * val)
{
this->val = reinterpret_cast<ptr_type>(val);
}
CValue(type * val) : val(val) {}
virtual void toString(std::stringstream & buf) {
buf << *val;
}
};
class CLabel
{
std::stringstream ss;
std::vector<CBaseValue *> valueList;
std::string format;
public:
CLabel() {};
void reset() {
format.clear();
ss.str("");
for(unsigned i = 0; i < valueList.size(); i++) {
delete valueList[i];
}
valueList.clear();
}
void setFormat(const char * fmt, ...) {
reset();
format = fmt;
va_list args;
va_start(args, fmt);
for(unsigned i = 0; i < format.size(); ++i) {
if(format[i] == '%') {
++i;
switch(fmt[i])
{
case 'd':
valueList.push_back(new CValue<unsigned int>( va_arg(args, void *) ));
break;
case 'f':
valueList.push_back(new CValue<float>( va_arg(args, void *) ));
break;
}
}
}
va_end(args);
}
std::string get() {
ss.str("");
unsigned count(0);
for(unsigned i = 0; i < format.size(); i++) {
if(format[i] == '%') {
i++; // ignore type specifiers, already polymorphically solved
valueList[count++]->toString(ss);
} else {
ss << format[i];
}
}
return ss.str();
}
~CLabel() {
reset();
}
};
int main() {
int test = 2;
float val = 3.14f;
CLabel myLabel;
myLabel.setFormat("Stringstream test, float: %f, and an int: %d \n", &val, &test);
std::cout << myLabel.get();
test = 3;
std::cout << myLabel.get();
system("pause");
}
You could do something relatively simple with std::bind or boost::bind. I'll leave it as an exercise on how to massage a C interface on top of this.
#include <functional>
int main() {
int test = 2;
float val = 3.14f;
std::function<int()> label = std::bind(
printf,
"Stringstream test, float: %f, and an int: %d \n",
std::ref(val),
std::ref(test));
label();
test = 3;
label();
}

Resources