my problem with vsprintf is that I can not obtain input arguments directly, I have to first get inputs one by one and save them in void**, then pass this void** to vsprintf(), it is all fine for windows, but when I come to 64bit linux, gcc cannot compile because it is not allowed to convert from void** to va_list, Is there anyone that can give me some help how I should do this under linux?
Can I create va_list dynamically in GCC?
void getInputArgs(char* str, char* format, ...)
{
va_list args;
va_start(args, format);
vsprintf(str, format, args);
va_end(args);
}
void process(void)
{
char s[256];
double tempValue;
char * tempString = NULL;
void ** args_ptr = NULL;
ArgFormatType format; //defined in the lib I used in the code
int numOfArgs = GetNumInputArgs(); // library func used in my code
if(numOfArgs>1)
{
args_ptr = (void**) malloc(sizeof(char)*(numOfArgs-1));
for(i=2; i<numOfArgs; i++)
{
format = GetArgType(); //library funcs
switch(format)
{
case ArgType_double:
CopyInDoubleArg(i, TRUE, &tempValue); //lib func
args_ptr[i-2] = (void*) (int)tempValue;
break;
case ArgType_char:
args_ptr[i-2]=NULL;
AllocInCharArg(i, TRUE, &tempString); //lib func
args_ptr[i-2]= tempString;
break;
}
}
}
getInputArgs(s, formatString, (va_list) args_ptr); //Here
// is the location where gcc cannot compile,
// Can I and how if I can create a va_list myself?
}
There is a way you can do this, but it is specific to gcc on Linux. It does work on Linux (tested) for both 32 and 64 bit builds.
DISCLAIMER: I am not endorsing using this code. It is not portable, it is hackish, and is quite frankly a precariously balanced elephant on a proverbial tightrope. I am merely demonstrating that it is possible to dynamically create a va_list using gcc, which is what the original question was asking.
With that said, the following article details how va_list works with the amd64 ABI: Amd64 and Va_arg.
With knowledge of the internal structure of the va_list struct, we can trick the va_arg macro into reading from a va_list that we construct ourselves:
#if (defined( __linux__) && defined(__x86_64__))
// AMD64 byte-aligns elements to 8 bytes
#define VLIST_CHUNK_SIZE 8
#else
#define VLIST_CHUNK_SIZE 4
#define _va_list_ptr _va_list
#endif
typedef struct {
va_list _va_list;
#if (defined( __linux__) && defined(__x86_64__))
void* _va_list_ptr;
#endif
} my_va_list;
void my_va_start(my_va_list* args, void* arg_list)
{
#if (defined(__linux__) && defined(__x86_64__))
/* va_args will read from the overflow area if the gp_offset
is greater than or equal to 48 (6 gp registers * 8 bytes/register)
and the fp_offset is greater than or equal to 304 (gp_offset +
16 fp registers * 16 bytes/register) */
args->_va_list[0].gp_offset = 48;
args->_va_list[0].fp_offset = 304;
args->_va_list[0].reg_save_area = NULL;
args->_va_list[0].overflow_arg_area = arg_list;
#endif
args->_va_list_ptr = arg_list;
}
void my_va_end(my_va_list* args)
{
free(args->_va_list_ptr);
}
typedef struct {
ArgFormatType type; // OP defined this enum for format
union {
int i;
// OTHER TYPES HERE
void* p;
} data;
} va_data;
Now, we can generate the va_list pointer (which is the same for both 64 bit and 32 bit builds) using something like your process() method or the following:
void* create_arg_pointer(va_data* arguments, unsigned int num_args) {
int i, arg_list_size = 0;
void* arg_list = NULL;
for (i=0; i < num_args; ++i)
{
unsigned int native_data_size, padded_size;
void *native_data, *vdata;
switch(arguments[i].type)
{
case ArgType_int:
native_data = &(arguments[i].data.i);
native_data_size = sizeof(arguments[i]->data.i);
break;
// OTHER TYPES HERE
case ArgType_string:
native_data = &(arguments[i].data.p);
native_data_size = sizeof(arguments[i]->data.p);
break;
default:
// error handling
continue;
}
// if needed, pad the size we will use for the argument in the va_list
for (padded_size = native_data_size; 0 != padded_size % VLIST_CHUNK_SIZE; padded_size++);
// reallocate more memory for the additional argument
arg_list = (char*)realloc(arg_list, arg_list_size + padded_size);
// save a pointer to the beginning of the free space for this argument
vdata = &(((char *)(arg_list))[arg_list_size]);
// increment the amount of allocated space (to provide the correct offset and size for next time)
arg_list_size += padded_size;
// set full padded length to 0 and copy the actual data into the location
memset(vdata, 0, padded_size);
memcpy(vdata, native_data, native_data_size);
}
return arg_list;
}
And finally, we can use it:
va_data data_args[2];
data_args[0].type = ArgType_int;
data_args[0].data.i = 42;
data_args[1].type = ArgType_string;
data_args[1].data.p = "hello world";
my_va_list args;
my_va_start(&args, create_arg_pointer(data_args, 2));
vprintf("format string %d %s", args._va_list);
my_va_end(&args);
And there you have it. It works mostly the same as the normal va_start and va_end macros, but lets you pass your own dynamically generated, byte-aligned pointer to be used instead of relying on the calling convention to set up your stack frame.
I have tried using libffi as mentioned somewhere else and it works.
Here below is the link , hope it can help others with similar issues.
Thanks again for all help I got here!
Link:
http://www.atmark-techno.com/~yashi/libffi.html -- simple example given
http://www.swig.org/Doc1.3/Varargs.html -- printf() and other examples given
The type of va_list is not void ** or anything similar with 64-bit gcc (on Intel x86/64 machines). On both Mac OS X 10.7.4 and on RHEL 5, there is no header stdarg.h in /usr/include. Consider the following code:
#include <stdarg.h>
#include <stdio.h>
int main(void)
{
printf("sizeof(va_list) = %zu\n", sizeof(va_list));
return 0;
}
The output on both RHEL 5 and Mac OS X 10.7 with a 64-bit compilation is:
sizeof(va_list) = 24
With a 32-bit compilation, the output on each platform is:
sizeof(va_list) = 4
(You may take it that I was surprised to find this much discrepancy between the 32-bit and 64-bit versions. I was expecting a value between 12 and 24 for the 32-bit version.)
So, the type is opaque; you can't even find a header that tells you anything about; and it is much bigger than a single pointer on 64-bit machines.
Even if your code works on some machines, it is very, very far from guaranteed to work everywhere.
The GCC 4.7.1 manual does not mention any functions that allow you to build a va_list at runtime.
Following class works for me:
class VaList
{
va_list _arguments;
public:
explicit inline VaList(const void * pDummy, ...)
{
va_start(_arguments, pDummy);
}
inline operator va_list &()
{
return _arguments;
}
inline operator const va_list &() const
{
return _arguments;
}
inline ~VaList()
{
va_end(_arguments);
}
};
and it can be used like this:
void v(const char * format, const va_list & arguments)
{
vprintf(format, const_cast<va_list &>(arguments));
}
...
v("%d\n", VaList("", 1)); // Uses VaList::operator va_list &()
v("%d %d\n", VaList(nullptr, 2, 3)); // Uses VaList::operator va_list &()
vprintf("%s %s %s\n", VaList("", "Works", "here", "too!"));
const VaList args(NULL, 4, 5, "howdy", "there");
v("%d %d %s %s\n", args); // Uses VaList::operator const va_list &() const
The first dummy parameter can be any kind of pointer, it is only used to compute the address of the following arguments.
The same can of course be done in C too but not so niftily (use pointer instead of reference)!
Simple example of using VaList to construct a dynamic va_list:
static void VectorToVaList(const std::vector<int> & v, va_list & t)
{
switch (v.size())
{
case 1: va_copy(t, VaList("", v[0])); return;
case 2: va_copy(t, VaList("", v[0], v[1])); return;
case 3: va_copy(t, VaList("", v[0], v[1], v[2])); return;
case 4: va_copy(t, VaList("", v[0], v[1], v[2], v[3])); return;
// etc
}
throw std::out_of_range("Out of range vector size!");
}
and usage:
va_list t;
VectorToVaList(std::vector<int>{ 1, 2, 3, 4 }, t);
vprintf("%d %d %d %d\n", t);
If the problem you're trying to solve is inserting passing arbitrary types to a function in va_list style, then, consider using union:
#include <iostream>
#include <cstdarg>
union ARG
{
int d;
char* s;
double f;
};
int main()
{
printf("%d %s %f \n", 1, "two", 3.1415 );
// Output: 1 two 3.141500
char format[ 1024 ] = "%d %s %f\n";
ARG args[ 5 ] = { };
args[ 0 ].d = 1;
args[ 1 ].s = "two";
args[ 2 ].f = 3.1415;
printf( format, args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ], args[ 4 ] );
// Output: 1 two 3.141500
return 0;
}
Some things you'll note about my solution:
No attempt is made to produce the correct number of arguments. i.e. I oversupply the arguments, but, most functions will look at the first parameter to determine how to handle the rest (i.e. format)
I didn't bother dynamically create the format, but, it is a trivial exercise to build a routine that dynamically populates format and args.
Tested this on:
- Ubuntu, g++
- Android NDK
I did some more testing, and, confirmed #PeterCoordes comments about this answer not working for double precision.
Related
Below is a function returning a pointer to string, and this function is polymorphism, which means it could return any classmate's name.
const char* get_classmate_name(void);
Here is a task that asks you to put/get classmate's information into/from in info.txt under each classmate's folder, for example:
/good_class/Anderson/info.txt/
/good_class/Cindy/info.txt/
/good_class/Lily/info.txt/
I attempted to define those attributes with define macro:
#define CLASS_PATH "/good_class"
#define CLASSMATE_PATH CLASS_PATH##"/"get_classmate_name() //wrong
#define CLASSMATE_INFO_PATH CLASSMATE_PATH"/info.txt"
Obviously, the second macro is wrong because it's not allowed to concatenate symbolic constant, in this case CLASS_PATH, with const char* by using ## operator.
I want program to be simple such like
static bool folder_is_found(const char* folder_path);
int main(void)
{
if ((!folder_is_found(CLASS_PATH)) || (!folder_is_found(CLASSMATE_PATH)))
{
reutrun -1;
}
const char* classmate_height = get_classmate_height(CLASSMATE_INFO_PATH);
printf("classmate's height is :%s", classmate_height);
return 0;
}
How to correctly define CLASSMATE_PATH?
You cannot define CLASSMATE_PATH as a macro, because the classmate's name (obtained via get_classmate_name()) is only known at run-time, and not at compile-time.
A possible function you may use in order to concatenate multiple strings at once is snprintf():
char* get_classmate_path(void) {
const size_t max_path_size = 200;
char* res = malloc(max_path_size);
if (res == NULL) {
return NULL;
}
int bytes = snprintf(res, max_path_size, "%s/%s", CLASS_PATH, get_classmate_name());
if (bytes < 0 || (size_t) bytes >= max_path_size) {
free(res);
return NULL;
}
return res;
}
int main(void) {
printf("Classmate path: %s\n", get_classmate_path());
// POSSIBLE OUTPUT: Classmate path: /good_class/Anderson
}
It is trivial, then, to concatenate even more strings together:
char* get_classmate_info_path(void) {
const size_t max_path_size = 200;
char* res = malloc(max_path_size);
if (res == NULL) {
return NULL;
}
int bytes = snprintf(res, max_path_size, "%s/%s/info.txt", CLASS_PATH, get_classmate_name());
if (bytes < 0 || (size_t) bytes >= max_path_size) {
free(res);
return NULL;
}
return res;
}
NOTE: the previous examples make use of dynamic memory allocation (via malloc()); you may want to have a detailed look at how dynamic memory works in C (for instance, you should call free() after you're done using the result of get_classmate_path(), but that's out of answer's scope).
NOTE 2: the previous functions (get_classmate_path() and get_classmate_info_path()) may return NULL in case of errors.
Macros are not functions even if sometimes they look similar.
Macros are evaluated before the C code compilation starts. The preprocessor does not know anything about C language.
## concatenated tokens in macro during the preprocessing stage (ie before C code compilation). ## does something completely different than a concatenation of the adjacent string literals (both have to be string literals and concatenation happens compile time, not run time).
You need to use functions instead of macros to achieve what you want.
I have an application that builds file path names through a series of string concatenations using pieces of text to create a complete file path name.
The question is whether an approach to handle concatenating a small but arbitrary number of strings of text together depends on Undefined Behavior for success.
Is the order of evaluation of a series of nested functions guaranteed or not?
I found this question Nested function calls order of evaluation however it seems to be more about multiple functions in the argument list rather than a sequence of nesting functions.
Please excuse the names in the following code samples. It is congruent with the rest of the source code and I am testing things out a bit first.
My first cut on the need to concatenate several strings was a function that looked like the following which would concatenate up to three text strings into a single string.
typedef wchar_t TCHAR;
TCHAR *RflCatFilePath(TCHAR *tszDest, int nDestLen, TCHAR *tszPath, TCHAR *tszPath2, TCHAR *tszFileName)
{
if (tszDest && nDestLen > 0) {
TCHAR *pDest = tszDest;
TCHAR *pLast = tszDest;
*pDest = 0; // ensure empty string if no path data provided.
if (tszPath) for (pDest = pLast; nDestLen > 0 && (*pDest++ = *tszPath++); nDestLen--) pLast = pDest;
if (tszPath2) for (pDest = pLast; nDestLen > 0 && (*pDest++ = *tszPath2++); nDestLen--) pLast = pDest;
if (tszFileName) for (pDest = pLast; nDestLen > 0 && (*pDest++ = *tszFileName++); nDestLen--) pLast = pDest;
}
return tszDest;
}
Then I ran into a case where I had four pieces of text to put together.
Thinking through this it seemed that most probably there would also be a case for five that would be uncovered shortly so I wondered if there was a different way for an arbitrary number of strings.
What I came up with is two functions as follows.
typedef wchar_t TCHAR;
typedef struct {
TCHAR *pDest;
TCHAR *pLast;
int destLen;
} RflCatStruct;
RflCatStruct RflCatFilePathX(const TCHAR *pPath, RflCatStruct x)
{
TCHAR *pDest = x.pLast;
if (pDest && pPath) for ( ; x.destLen > 0 && (*pDest++ = *pPath++); x.destLen--) x.pLast = pDest;
return x;
}
RflCatStruct RflCatFilePathY(TCHAR *buffDest, int nLen, const TCHAR *pPath)
{
RflCatStruct x = { 0 };
TCHAR *pDest = x.pDest = buffDest;
x.pLast = buffDest;
x.destLen = nLen;
if (buffDest && nLen > 0) { // ensure there is room for at least one character.
*pDest = 0; // ensure empty string if no path data provided.
if (pPath) for (pDest = x.pLast; x.destLen > 0 && (*pDest++ = *pPath++); x.destLen--) x.pLast = pDest;
}
return x;
}
Examples of using these two functions is as follows. This code with the two functions appears to work fine with Visual Studio 2013.
TCHAR buffDest[512] = { 0 };
TCHAR *pPath = L"C:\\flashdisk\\ncr\\database";
TCHAR *pPath2 = L"\\";
TCHAR *pFilename = L"filename.ext";
RflCatFilePathX(pFilename, RflCatFilePathX(pPath2, RflCatFilePathY(buffDest, 512, pPath)));
printf("dest t = \"%S\"\n", buffDest);
printf("dest t = \"%S\"\n", RflCatFilePathX(pFilename, RflCatFilePathX(pPath2, RflCatFilePathY(buffDest, 512, pFilename))).pDest);
RflCatStruct dStr = RflCatFilePathX(pPath2, RflCatFilePathY(buffDest, 512, pPath));
// other stuff then
printf("dest t = \"%S\"\n", RflCatFilePathX(pFilename, dStr).pDest);
Arguments to a function call are completely evaluated before the function is invoked. So the calls to RflCatFilePath* will be evaluated in the expected order. (This is guaranteed by ยง6.5.2.2/10: "There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call.")
As indicated in a comment, the snprintf function is likely to be a better choice for this problem. (asprintf would be even better, and there is a freely available shim for it which works on Windows.) The only problem with snprintf is that you may have to call it twice. It always returns the number of bytes which would have been stored in the buffer had there been enough space, so if the return value is not less than the size of the buffer, you will need to allocate a larger buffer (whose size you now know) and call snprintf again.
asprintf does that for you, but it is a BSD/Gnu extension to the standard library.
In the case of concatenating filepaths, there is a maximum string length supported by the operating system/file system, and you should be able to find out what it is (although it might require OS-specific calls on non-Posix systems). So it might well be reasonable to simply return an error indication if the concatenation does not fit into a 512-byte buffer.
Just for fun, I include a recursive varargs concatenator:
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
static char* concat_helper(size_t accum, char* chunk, va_list ap) {
if (chunk) {
size_t chunklen = strlen(chunk);
char* next_chunk = va_arg(ap, char*);
char* retval = concat_helper(accum + chunklen, next_chunk, ap);
memcpy(retval + accum, chunk, chunklen);
return retval;
} else {
char* retval = malloc(accum + 1);
retval[accum] = 0;
return retval;
}
}
char* concat_list(char* chunk, ...) {
va_list ap;
va_start(ap, chunk);
char* retval = concat_helper(0, chunk, ap);
va_end(ap);
return retval;
}
Since concat_list is a varargs function, you need to supply (char*)NULL at the end of the arguments. On the other hand, you don't need to repeat the function name for each new argument. So an example call might be:
concat_list(pPath, pPath2, pFilename, (char*)0);
(I suppose you need a wchar_t* version but the changes should be obvious. Watch out for the malloc.) For production purposes, the recursion should probably be replaced by an iterative version which traverses the argument list twice (see va_copy) but I've always been fond of the "there-and-back" recursion pattern.
my problem with vsprintf is that I can not obtain input arguments directly, I have to first get inputs one by one and save them in void**, then pass this void** to vsprintf(), it is all fine for windows, but when I come to 64bit linux, gcc cannot compile because it is not allowed to convert from void** to va_list, Is there anyone can give me some help how I should do under linux?
part of my code is:
void getInputArgs(char* str, char* format, ...)
{
va_list args;
va_start(args, format);
vsprintf(str, format, args);
va_end(args);
}
void process(void)
{
char s[256];
double tempValue;
char * tempString = NULL;
void ** args_ptr =NULL;
ArgFormatType format; //defined in the lib I used in the code
int numOfArgs = GetNumInputArgs(); // library func used in my code
if(numOfArgs>1)
{
args_ptr = (void**) malloc(sizeof(char)*(numOfArgs-1));
for(i=2; i<numOfArgs; i++)
{
format = GetArgType(); //library funcs
switch(format)
{
case ArgType_double:
CopyInDoubleArg(i, TRUE, &tempValue); //lib func
args_ptr[i-2] = (void*) (int)tempValue;
break;
case ArgType_char:
args_ptr[i-2]=NULL;
AllocInCharArg(i, TRUE, &tempString); //lib func
args_ptr[i-2]= tempString;
break;
}
}
}
getInputArgs(s, formatString, (va_list) args_ptr); /////Here is the location where gcc cannot compile
}
Many Many thanks!!
The problem is, your function gets ..., but you are passing it a va_list. ... is used for a usage like this:
getInputArgs(s, formatString, arg1, arg2, arg3, arg4 /* etc */);
and it won't work with va_list. Unfortunately, there is not an easy way to create a va_list from different parameters instead of getting it from .... See this question for example.
What you should do is to change the way you want to print to the string.
You can have:
char s[256];
int so_far = 0;
And in your for loop instead of something like this:
CopyInDoubleArg(i, TRUE, &tempValue); //lib func
args_ptr[i-2] = (void*) (int)tempValue;
You write:
CopyInDoubleArg(i, TRUE, &tempValue); //lib func
if (so_far < 256) /* 256 is the maximum length of s */
so_far += snprintf(s + so_far, 256 - so_far, "%lf", tempValue);
Something along these lines. This way, you create the string one by one, appending each element to the previous, instead of trying to make it all at once.
I've noticed that at several places in our code base we use dynamically expanding arrays, i.e. a base array coupled with an element counter and a "max elements" value.
What I want to do is replace these with a common data structure and utility functions, for the usual object-oriented reasons.
The array elements can be either basic data types or structs, I need fast random access to the elements, and preferably a type-safe implementation.
So, basically, what I would like to use is an STL vector, but the code base is restricted to C89 so I have to come up with something else :-)
I gave it some thought and whipped up this initial draft, just to show what I'm aiming at:
/* Type-safe dynamic list in C89 */
#define list_declare(type) typedef struct _##type##_list_t { type * base_array; size_t elements; size_t max_size; } type##_list_t
#define list(type) type##_list_t
#define list_new(type, initial_size) { calloc(initial_size, sizeof(type)), 0, initial_size }
#define list_free(list) free(list.base_array)
#define list_set(list, place, element) if ( list.elements < list.max_size ) { list.base_array[place] = element; } else { /* Array index out of bounds */ }
#define list_add(list, element) if ( list.elements < list.max_size ) { list.base_array[list.elements++] = element; } else { /* Expand array then add */ }
#define list_get(list, n) list.base_array[n]
/* Sample usage: */
list_declare(int);
int main(void)
{
list(int) integers = list_new(int, 10);
printf("list[0] = %d\n", list_get(integers, 0));
list_add(integers, 4);
printf("list[0] = %d\n", list_get(integers, 0));
list_set(integers, 0, 3);
printf("list[0] = %d\n", list_get(integers, 0));
list_free(integers);
return EXIT_SUCCESS;
}
...however, there must be someone else who has done this before. I'm aware of the FreeBSD sys/queue.h implementation of a similar concept for some different queues, but I can't find anything like that for arrays.
Is anyone here any wiser?
glib provides an GArray type, which implements a dynamically growing array. If you can use external 3rd party libraries, glib is almost always a good choice as "standard" library for C. It provides types for all basic data structures, for unicode strings, for date and time values, and so on.
here a simple vector-replacement, its ONE function for all, its strictly C89 and threadsafe;
libs are too difficult for me, i use my own;
no performance, but easy to use
/* owner-structs too */
typedef struct {
char name[20],city[20];
int salary;
} My,*Myp;
typedef char Str80[80];
/* add here your type with its size */
typedef enum {SPTR,INT=sizeof(int),DOUBLE=sizeof(double),S80=sizeof(Str80),MY=sizeof(My)} TSizes;
typedef enum {ADD,LOOP,COUNT,FREE,GETAT,GET,REMOVEAT,REMOVE} Ops;
void *dynarray(char ***root,TSizes ts,Ops op,void *in,void *out)
{
size_t d=0,s=in?ts?ts:strlen((char*)in)+1:0;
char **r=*root;
while( r && *r++ ) ++d;
switch(op) {
case ADD: if( !*root ) *root=calloc(1,sizeof r);
*root=realloc(*root,(d+2)*sizeof r);
memmove((*root)+1,*root,(d+1)*sizeof r);
memcpy(**root=malloc(s),in,s);
break;
case LOOP: while( d-- ) ((void (*)(char*))in)((*root)[d]); break;
case COUNT: return *(int*)out=d,out;
case FREE: if(r) {
++d; while( d-- ) realloc((*root)[d],0);
free(*root);*root=0;
} break;
case GETAT: { size_t i=*(size_t*)in;
if(r && i<=--d)
return (*root)[d-i];
} break;
case GET: { int i=-1;
while( ++i,d-- )
if( !(ts?memcmp:strncmp)(in,(*root)[d],s) )
return *(int*)out=i,out;
return *(int*)out=-1,out;
}
case REMOVEAT: { size_t i=*(size_t*)in;
if(r && i<=--d) {
free((*root)[d-i]);
memmove(&(*root)[d-i],&(*root)[d-i+1],(d-i+1)*sizeof r);
return in;
}
} break;
case REMOVE: while( *(int*)dynarray(root,ts,GET,in,&d)>=0 )
dynarray(root,ts,REMOVEAT,&d,0);
}
return 0;
}
void outmy(Myp s)
{
printf("\n%s,%s,%d",s->name,s->city,s->salary);
}
main()
{
My z[]={{"Buffet","Omaha",INT_MAX},{"Jobs","Palo Alto",1},{"Madoff","NYC",INT_MIN}};
Str80 y[]={ "123","456","7890" };
char **ptr=0;
int x=1;
/* precondition for first use: ptr==NULL */
dynarray(&ptr,SPTR,ADD,"test1.txt",0);
dynarray(&ptr,SPTR,ADD,"test2.txt",0);
dynarray(&ptr,SPTR,ADD,"t3.txt",0);
dynarray(&ptr,SPTR,REMOVEAT,&x,0); /* remove at index/key ==1 */
dynarray(&ptr,SPTR,REMOVE,"test1.txt",0);
dynarray(&ptr,SPTR,GET,"t3.txt",&x);
dynarray(&ptr,SPTR,LOOP,puts,0);
/* another option for enumerating */
dynarray(&ptr,SPTR,COUNT,0,&x);
while( x-- )
puts(ptr[x]);
dynarray(&ptr,SPTR,FREE,0,0); /* frees all mallocs and set ptr to NULL */
/* start for another (user)type */
dynarray(&ptr,S80,ADD,y[0],0);
dynarray(&ptr,S80,ADD,y[1],0);
dynarray(&ptr,S80,ADD,y[2],0);
dynarray(&ptr,S80,ADD,y[0],0);
dynarray(&ptr,S80,LOOP,puts,0);
dynarray(&ptr,S80,FREE,0,0); /* frees all mallocs and set ptr to NULL */
/* start for another (user)struct-type */
dynarray(&ptr,MY,ADD,&z[0],0);
dynarray(&ptr,MY,ADD,&z[1],0);
dynarray(&ptr,MY,ADD,&z[2],0);
dynarray(&ptr,MY,ADD,&z[0],0);
dynarray(&ptr,MY,LOOP,outmy,0);
dynarray(&ptr,MY,FREE,0,0);
return 0;
}
There is sglib, which implements various lists,hashmaps and rbtrees in a generic fashion (i.e. by specializing over a type). There is also a fast sorting function for arrays:
http://sglib.sourceforge.net/
qLibc implements a vector in pure C. The data structure allows it to store any type of object like (void *object) and it provides convenient wrappers for string, formatted string and integer types.
Here's a sample code for your idea.
qvector_t *vector = qvector(QVECTOR_OPT_THREADSAFE);
vector->addstr(vector, "Hello");
vector->addstrf(vector, "World %d", 123);
char *finalstring = vector->tostring(vector);
printf("%s", finalstring);
free(finalstring)
vector->free(vector);
for object type:
int a = 1, b = 2;
qvector_t *vector = qvector(QVECTOR_OPT_THREADSAFE);
vector->add(vector, (void *)&a, sizeof(int));
vector->add(vector, (void *)&b, sizeof(int));
int *finalarray = vector->toarray(vector);
printf("a = %d, b = %d", finalarray[0], finalarray[1]);
free(finalarray)
vector->free(vector);
Note) I made this sample code just for your reference, copying from its example code.
it might have typo errors.
You can check out the Full API reference at http://wolkykim.github.io/qlibc/
I'm using the following macro implementation without problems so far. It isn't a complete implementation but grows the array automatically :
#define DECLARE_DYN_ARRAY(T) \
typedef struct \
{ \
T *buf; \
size_t n; \
size_t reserved; \
} T ## Array;
#define DYN_ARRAY(T) T ## Array
#define DYN_ADD(array, value, errorLabel) DYN_ADD_REALLOC(array, value, errorLabel, realloc)
#define DYN_ADD_REALLOC(array, value, errorLabel, realloc) \
{ \
if ((array).n >= (array).reserved) \
{ \
if (!(array).reserved) (array).reserved = 10; \
(array).reserved *= 2; \
void *ptr = realloc((array).buf, sizeof(*(array).buf)*(array).reserved); \
if (!ptr) goto errorLabel; \
(array).buf = ptr; \
} \
(array).buf[(array).n++] = value; \
}
To use you first write: DECLARE_DYN_ARRAY(YourType)
To declare variables you write DYN_ARRAY(YourType) array = {0}.
You add elements with DYN_ADD(array, element, errorLabel).
You access elements with array.buf[i].
You get the number of elements with array.n.
When done you free it with free(array.buf) (or whatever function you used to allocate it.)
I usually roll my own code for purposes such as this, like you did. It's not particularly difficult, but having type safety etc. is not easily achievable without a whole OO framework.
As mentioned before, glib offers what you need - if glib2 is too big for you, you could still go with glib1.2. It's quite old, but doesn't have external dependencies (except for pthread if you need thread support). The code can also be integrated into larger projects, if necessary. It's LGPL licensed.
Personally, I prefer "Gena" library. It closely resembles stl::vector in pure C89.
It is comfortable to use because you can:
Access vector elements just like plain C arrays: vec[k][j];
Have multi-dimentional arrays;
Copy vectors;
Instantiate necessary vector types once in a separate module, instead of doing this every time you needed a vector;
You can choose how to pass values into a vector and how to return them from it: by value or by pointer.
You can check it out here:
https://github.com/cher-nov/Gena
I need to dump the certain things into a text file and same has needs to be displayed on screen. (I'm telling about a C program utiltiy)
The menu option looks like following,
1. display AA parameters
2. display BB parameters
3. display CC parameters
4. dump all
5. Exit
Select option >
If they select 1/2/3, it just needs to displayed on screen only or if they select option #4,it need to display all the parameters one by one and same needs to dumped in a .txt file.
I know, we can use the printf and fprintf functions to display on screen and write it to text file respectively. The thing is that I've display more that 20 parameters and each have at least 20 sub-parameters.
I'm currently implemented as below,
printf ( "Starting serial number [%ld]\n",
serial_info_p->start_int_idx);
fprintf(file_p, "Starting serial number [%ld]\n",
serial_info_p->start_int_idx)
printf ( "Current Serial number [%d]\n",
serial_info_p->current_int_idx);
fprintf(file_p, "Current Serial number [%d]\n",
serial_info_p->current_int_idx);
Is there an easiest way to implement this to cut down the number of lines of code?
Edit: the C++ tag seems misleading, can someone remove it please? thanks :)
I use variadic macros to customize printf and friends.
I would write something like this:
#define tee(fp,fmt, ...) \
{ \
printf (fmt, __VA_ARGS__); \
fprintf (fp, fmt, __VA_ARGS__); \
}
(the name comes from the tee(1) utility)
Something like this allows you to add any number of output streams, and allows changing them at runtime simply by modifying the PrintTarget linked list.
/** gcc -Wall -o print_target print_target.c && ./print_target */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct PrintTarget* PrintTargetp;
void* xmalloc (size_t size);
PrintTargetp pntCreate (PrintTargetp head, FILE* target);
void pntDestroy (PrintTargetp list);
typedef struct PrintTarget
{
FILE* target;
PrintTargetp next;
} PrintTarget;
void myPrintf (PrintTargetp streams, char* format, ...)
{
va_list args;
va_start(args, format);
while (streams)
{
vfprintf(streams->target, format, args);
streams = streams->next;
}
va_end(args);
}
int main(void)
{
PrintTargetp streams = pntCreate(NULL, stdout);
streams = pntCreate(streams, fopen("somefile.txt", "a+")); //XXX IO errors?
myPrintf(streams, "blah blah blah...\n");
pntDestroy(streams);
return 0;
}
Here's a definition of auxiliary functions:
PrintTargetp pntCreate (PrintTargetp head, FILE* target)
{
PrintTargetp node = xmalloc(sizeof(PrintTarget));
node->target = target;
node->next = head;
return node;
}
void pntDestroy (PrintTargetp list)
{
while (list)
{
PrintTargetp next = list->next;
free(list);
list = next;
//XXX cycles?
//XXX close files?
}
}
void* xmalloc (size_t size)
{
void* p = malloc(size);
if (p == NULL)
{
fputs("malloc error\n", stderr);
abort();
}
return p;
}
You could also just pipe the output of your prorgam to the tee(1) command.
If you're writing a console application, you should be able to output to the screen (standard output) using something like:
fprintf(stdout, "Hello World\n");
This should enable you to move the code that prints your data to its own function, and to pass in a FILE* for it to print to. Then the function can print to the screen if you pass "stdout", or to a file if you pass in a different FILE*, e.g.:
void print_my_stuff(FILE* file) {
fprintf( file,"Starting serial number [%ld]\n", serial_info_p->start_int_idx);
fprintf(file, "Current Serial number [%d]\n", serial_info_p->current_int_idx);
.
.
.
}
Edit: I didn't notice you needed a C solution. I'll leave this answer for reference, but it obviously requires C++.
You could create a new stream class that sends the output to two streams. I found an implementation of this at http://www.cs.technion.ac.il/~imaman/programs/teestream.html. I haven't tried it, but it should work.
Here's the code from the link:
#include <iostream>
#include <fstream>
template<typename Elem, typename Traits = std::char_traits<Elem> >
struct basic_TeeStream : std::basic_ostream<Elem,Traits>
{
typedef std::basic_ostream<Elem,Traits> SuperType;
basic_TeeStream(std::ostream& o1, std::ostream& o2)
: SuperType(o1.rdbuf()), o1_(o1), o2_(o2) { }
basic_TeeStream& operator<<(SuperType& (__cdecl *manip)(SuperType& ))
{
o1_ << manip;
o2_ << manip;
return *this;
}
template<typename T>
basic_TeeStream& operator<<(const T& t)
{
o1_ << t;
o2_ << t;
return *this;
}
private:
std::ostream& o1_;
std::ostream& o2_;
};
typedef basic_TeeStream<char> TeeStream;
You would use it like this:
ofstream f("stackoverflow.txt");
TeeStream ts(std::cout, f);
ts << "Jon Skeet" << std::endl; // "Jon Skeet" is sent to TWO streams
I'd go more radical than what people have suggested so far, but maybe it is too much for you. (The 'inline' keyword is C99; you can omit it without much consequence if you code to C89.)
/*
** These could be omitted - unless you get still more radical and create
** the format strings at run-time, so you can adapt the %-24s to the
** longest tag you actually have. Plus, with the strings all here, when
** you change the length from 24 to 30, you are less likely to overlook one!
*/
static const char fmt_int[] = "%-24s [%d]\n";
static const char fmt_long[] = "%-24s [%ld]\n";
static const char fmt_str[] = "%-24s [%s]\n"; /* Plausible extra ... */
static inline void print_long(FILE *fp, const char *tag, long value)
{
fprintf(fp, fmt_long, tag, value);
}
static inline void print_int(FILE *fp, const char *tag, int value)
{
fprintf(fp, fmt_int, tag, value);
}
static inline void print_str(FILE *fp, const char *tag, const char *value)
{
fprintf(fp, fmt_str, tag, value);
}
static void dump_data(FILE *fp, const serial_info_t *info)
{
dump_long("Starting serial number", info->start_int_idx);
dump_int( "Current Serial number", info->current_int_idx);
/* ... and similar ... */
}
Then the calling code would call dump_data() once (with argument stdout) for options 1, 2, 3 and twice (once with stdout, once with file pointer for output file) for option 4.
If the number of parameters got truly huge (into the multiple hundreds), I'd even go as far as to consider a data structure which encoded type and offset information (offsetof from <stddef.h>) and pointers to functions and such like, so that there would be just a loop in dump_data() iterating over a structure which encodes all the necessary information.
You could also simplify life by using the same basic integer type (long in your example) for all the integer members of the data structure.
Fred Brooks in "Mythical Man Month" - a book well worth reading if you've not already done so, but make sure you read the Twentieth Anniversary edition - says in Chapter 9:
Show me your flowcharts [code] and conceal your tables [data structures], and I shall continue to be mystified. Show me your tables, and I won't usually need your flowcharts; they'll be obvious.
A table-driven version of this code could end up saving space, as well as frustration when having to change a hundred related functions in the same way whereas a simple change in the tabular data could have fixed the whole lot.
#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0]))
FILE *f = fopen("somefile.txt", "a+");
FILE *fp[] = { stdout, f };
int i = 0;
for (i = 0; i < ARRAY_LEN(fp); i++) {
fprintf(fp[i], "Starting serial number [%ld]\n", serial_info_p->start_int_idx);
fprintf(fp[i], "Current serial number [%ld]\n", serial_info_p->start_int_idx);
}
fclose(f);