Use of void pointers (void*) and templates

closed account (S6k9GNh0)
Today, I was thinking about the uses of void* pointers and if there was any occasion where it would look better in C++. I couldn't think of any and I thought I'd explain why.

In C, the use of void pointers can be extremely useful. It can be used as a universally generic pointer that can point to any type. The limitation to this is that you must know the type that the void pointer was casted from in order to obtain data from it (which usually isn't that bad). Here's example using a simple list struct.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//Credit towards GLib I guess since they're the ones who helped me understand the concept of the void pointer years ago.
#include <stdlib.h>

typedef struct _LList
{
   void *data;
   struct _LList * next;
} LList;

//Returns new beginning to the list.
LList* list_prepend(LList* _node, void *_data)
{
   //NOTE! This is a memory leak! Be sure to free when in practice!
   LList * tmp = (LList*)malloc(sizeof(LList));
   tmp->data = _data;
   tmp->next = _node;
   return tmp;
}

int main()
{
   LList *myList = NULL;
   int someType = 324;

   myList = list_prepend(myList, (void*)&someType);
   //Technically, the list can hold any data type.
   //Because of this, the container lacks consistency and can be more confusing.
}


This shows that a struct that resembles a simple list node. Notice that in the struct, we have a dataype of void* that holds our data.

One may ask how we hold data if we don't know the data type? There are two answers actually. In actuality, you'll know the data type by the time you create the list so you can implement a list specific to your datatype, especially if they're non-primitive. You can imagine how annoying this would be. Or, my favorite C way, is to use void* pointers that can hold any pointer type! This gives a more dynamic view on basic C containers which you'll find a lot in glib and a few other general C libraries.

Another may ask how we handle the data if we don't know what data type it is? There is only one answer: You don't. The purpose of the list above is to handle pointers to data, not data themselves. There are a few rare occasions where handling data using void pointers through abstract functions is considered good but I honestly don't know about them. I'm sure GTK+ does it a few times. NOTE: TODO: FIXME: This isn't exactly what I'm wanting to say and is factually not right. In Win32, they use "window handles" (HWND) which happen to be void pointers to keep the contents of the window struct away from the Win32 developer. Although I'm ready to say you shouldn't do this ever, I don't have the experience to say that.

Now, note that this seems rather lumpy. There is no datatype consistency in the list e.g. you can have an integer that links to a struct, etc. Also, as I am a fan of beautifying code as much as possible, you must cast the pointer you wish to give to any of the list functions to void* first.

Here at C++, we don't settle for lumpy (actually, we do more often than one would know...) or code uglification (fancy word). If we know the data type at the time of List use, why can't we just pass the datatype itself to the list or use a macro or something? Well in C, we can use macros but even this isn't recommended really and can be rather messy. In C++, we have this awesome thing called templates which are heavily underused and often overused. So, this ugly void pointer struct thingy can be turned into:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
template<typename X>
struct LList
{
private:
        struct Link
        {
	        Link(X _data, Link * _next) : data(_data), next(_next) {}
	        X data;
	        Link *next;
        };

	Link *_first;
public:
	LList() : _first(NULL) {}

	~LList() {} //OMG LEAK! Make sure to delete the variables when in practice!

        void Prepend(X _data)
        {
                Link *tmp = _first;
                Link *newLink = new Link(_data, tmp); //This is a potential leak...
                //A safer approach would be to use smart pointers in case the list is broken.
		_first = newLink;
        }
};

int main()
{
	LList<int> tmpList;
	tmpList.Prepend(32432); tmpList.Prepend(3243);
        //The list may only contain types of int!
}



Read below if you want, was more of me just rambling...
To be fair, they are not the same thing however void pointers and templates can somewhat relate to each other. void pointers are used in C where templates are often used in C++ almost to a 1 to 1 ratio (if you were to count c function pointers as a void pointer WHICH IT IS NOT BUT JUST SAYING. In C, they use callbacks, where in C++, we can use class templates. Would anyone care to disagree?

NOTE: Doesn't really feel like an article but whatever...
Last edited on
Even that isn't a really good use of void pointers. Templates are better simulated with macros:

1
2
3
4
5
6
7
8
#define LLIST(structname,T)   \
typedef struct structname  \
{  \
  T* data;  \
  struct structname* next;  \
}

LLIST(LList,int);  //etc 
closed account (S6k9GNh0)
I mentioned it, I just didn't demonstrate it. Wasn't sure how to go about it properly to be honest but it seems pretty simple.
Last edited on
that's what I get for skimming and not actually reading.
void* might be useful for implementation of heterogenous data structures in a library code that need not be coupled tightly to user types. E.g. it must accept any user created object and operate on that. Things like e.g. garbage collector implementation or custom memory allocators, etc. These things often operate on "raw" memory, and the type information is useless there. Templates would only bloat the code. So void* is a good choice, then.
Last edited on
closed account (S6k9GNh0)
Yeah, after a bit more thought, I don't like the original post.. I speak of it as if void pointers and templates are interchangeable, which in some cases they are but not always. I also seem to be favoring C++ (noted by another friend who read the post). Although there is some useful information, the impression it gives is rather off. I won't take down but, just saying...
To get compile time encapsulation in C, declare a structure in the header:
1
2
struct encapsulated_thingy;
struct encapsulated_thingy * make one(int);

and define it in the code:
1
2
3
4
5
6
struct encapsulated_thingy{
    int bigness;
}
struct encapsulated_thingy * make_one(int b){
    //blah blah blah malloc();
}

Someone who #includes thingy.h will not be able to allocate encapsulated_thingies or access members, except through the interface that thingy.h provides. Templates have to be done with either void * or macros with the gnu extension typeof().
EDIT: That may sound offtopic. What I mean is that C cannot provide both templates and encapsulation unless you screw with void pointers.
Last edited on
Topic archived. No new replies allowed.