1

I've been using void pointers to implement stacks using arrays. I know that before displaying the value pointed to by the void pointer, we need to cast it to suitable data type.

This is my stack structure

typedef struct {
    void **arr;   // array stack
    int size;    // size of the stack
    int top;    // top most element of the stack
    int count; // number of elements in the stack
} stack_T;

Say I've an int stack ( contents of stack -> arr is of data type int ) now. To display it's contents I can simply write a display()

void displayIntStack( stack_T *stack ) { 
    while ( !isEmpty( stack )) {
        printf( "%d ", *( int * )stack -> arr[ stack -> top] );
        pop( stack ); 
    }
}

I can call it using displayIntStack( stack ); and it'll display the contents of the int stack.

If I want to implement more generic display() function to display the stack contents of all data types without the need to write separate functions. How would I do it?

  • 1
    Until C11 there was nothing to do what you are attempting. C11 introduced the `_Generic()` macro that, to a limited degree, allows for declaring options for handing each type you provide a definition for. Other than that, there isn't this type of type-overloading or deduction in C. C++ provides that ability. – David C. Rankin Dec 13 '20 at 05:19
  • I get it now.Thank you, @DavidC.Rankin! –  Dec 13 '20 at 05:22
  • 1
    Here is a link with a with discussion on `_Generic()` [Generics for multiparameter C functions in C11](https://stackoverflow.com/q/17302913/3422102). Given its awkwardness in use -- I never found much use for it... – David C. Rankin Dec 13 '20 at 05:25
  • When I started with C, I always thought why would anyone ever need C++?! C has got everything ( naive me ). I know now why. I guess I'll just use C++. –  Dec 13 '20 at 05:35
  • 3
    C will do anything you need (and fast), C++ may make doing what you need quicker to develop to avoid writing the additional code, but at the cost of compile-time complexity, memory use and overhead. Don't get me wrong, there is a lot of nicities in C++ containers and overloading -- but there is a reason the Linux kernel is written in C `:)` – David C. Rankin Dec 13 '20 at 05:40
  • IMO, users tend to need a customize-able print. For `double`, a generic format would be `"%.17g"`, but that may be unsatisfactory when printing, say Voltage, (prefer engineering units), money (yes I know the issues with FP & money) or ... – chux - Reinstate Monica Dec 13 '20 at 06:32

4 Answers4

3

If I want to implement more generic display() function to display the stack contents of all data types without the need to write separate functions. How would I do it?

Add a member to stack_T that is a function pointer that does the printing. Set that .print() member when defining the stack instance.

typedef struct {
    void **arr;
    int size;
    int top;
    int count;
    int (*print)(void *);
} stack_T;

while (!isEmpty(stack)) {
   stack->print(stack->arr[stack->top]);
   pop(stack); 
}

You can provide a predefined set of .print() functions for int, double and the usual suspects. This approach lets the user create custom print functions for any type.


I'd also expect a way to apply this .print() to the stack without changing the stack.

Further: I think the best approach is to form an apply function for your stack type and negating the need for a .print() function member:

Pseudo code

int stack_apply(const stack_T *st, int (*f)(void *state, void *data), void *state) {
  for each node i in the stack
    int result = f(state, stack->arr[i]);
    if (result) return result;
  }
  return 0;
}

Now when you want to print:

stack_apply(st, my_print_function, my_print_state);

Or maybe a search function, or a max function, or a standard deviation function ....

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

From C17 standard document, Generic selection (6.5.1.1, page number: 56-57):

You can use type-generic expression:

#define cbrt(X) _Generic((X), \
long double: cbrtl, \
default: cbrt, \
float: cbrtf \
)(X) 

Let's assume, there are these functions :
displayIntStack(stack,int), displayFloatStack(stack,float), displayCharStack(stack, char).

Then, we define one function displayStack(stack,type) in the main().

#define displayStack(stack,type)  _Generic((type), int: displayIntStack, float: displayFloatStack, char:displayCharStack)(stack,type)

int main() {
displayStack(stack,1); // calls displayIntStack
displayStack(stack,1.2f); // calls displayFloatStack
displayStack(stack,'c'); // calls char:displayCharStack
  
}

Another way : Using macro function

#define displayStack(stack_T *stack, T) \
{                                       \
    while (!isEmpty(stack))             \
    {                                   \
        printf("%d ", *(T *)stack->arr[stack->top]);\
        pop(stack);                                   \
    }                                                 \
}

displayStack(stack_T *stack, int) ; // For integer data
displayStack(stack_T *stack, float) ; // for float data
displayStack(stack_T *stack, double) ; // For double data
displayStack(stack_T *stack, char) ; // For char data
Krishna Kanth Yenumula
  • 2,533
  • 2
  • 14
  • 26
  • Yes, but the point being you still have to write `displayIntStack` and `displayFloatStack` and `displayCharStack`. – David C. Rankin Dec 13 '20 at 05:45
  • Agree. But this method comes, close to the solution (I think) – Krishna Kanth Yenumula Dec 13 '20 at 05:56
  • @DavidC.Rankin What do you think of the second method, I mentioned – Krishna Kanth Yenumula Dec 13 '20 at 06:00
  • 2
    That's getting closer, but the downside is you end up with the entire `stack` (or whatever data structure you are using) written as a macro instead of code. That brings in all the inherent limitations (and name collisions) you can have with macros. `_Generic()` is fine for simple things like a generic `puts()`, etc.. You really stumble into the limitations when you attempt to make complex data handling generic. (kind of a "round-peg" "square-hole" deal) But don't get me wrong -- my hat is off to you for the effort. If you look at Linux kernel code -- a lot looks that way `:)` – David C. Rankin Dec 13 '20 at 06:10
0

Hmm maybe void pointer ? It will allow you to pass pointer to any kind of type;) then just parse and print the value hidden behind pointer

0

How do I write a generic display function in C to display void pointers of all data types?

You cannot do that reliably, unless your display function is given some way of display values. And mostly because C types are not known at run-time (they are erased): the same sequence of bits could be a char* string or some array of double-s (perhaps even inside some union). Consider perhaps learning Ocaml which has a stricter and somehow safer type system (and retain some type information at runtime).

BTW, you could store inside your "generic" stack_T both the sizeof and the alignof pointed values. Then you could have, with some coding pain, one less indirection by using flexible array members. This might be more efficient (in some cases) because of your CPU cache.

A possibility might be to pass a callback function to your display function. You could for example pass as argument a function pointer displaying the pointed values.

Declare first (for readability) a signature

 typedef void element_display_sigt (void*displayed_data);

Then declare your displaying function to take such a function pointer

 void display_stack(stack_T *stack, element_display_sigt*dispfun);

Actually, what you dream of are closures. They don't exist in C17. Check by reading n2176 (a draft C standard) and Modern C

In practice, your element_display_sigt would often take an extra argument, for example a FILE* to write to. And your display_stack would also have that extra argument.

FWIW, closures exist (and also anonymous functions) in modern C++, in Scheme, in Common Lisp, in Ocaml, in Haskell, in Lua. I heard that future C standards might add anonymous functions to C.

You could consider embedding an interpreter like Lua or GNU Guile (a nice Scheme implementation) in your application coded in C.

You might be interested in reading the Dragon book, about λ-calculus, and about garbage collection.

If you code for Linux, be aware of dlopen(3) and dlsym(3) : in some cases, it might be worthwhile to generate the C code of some plugin at runtime, then compile that code into a shared object library, then use that dlopen-ed plugin or shared object at runtime. My manydl.c example shows that this can be done thousands of times. Another possibility might be to use machine code generation libraries like GNU lightning or libgccjit.

Be also aware that GNU libc specifically still has (at end of 2020) a way of customizing printf

Another approach is to generate some (printing) C code at build time, with tools inspired from SWIG. You could either write your own GCC plugin generating printing code or, using GNU bison, write your own C code generator.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547