Void Pointers as variables?

Started by
7 comments, last by frob 8 years, 5 months ago
I was wondering about this:

I have this class called ApplicationWindow
class ApplicationWindow
{
public:
	ApplicationWindow();
	virtual ~ApplicationWindow();
	void *GetWindow();
	int32_t GetWidth();
	int32_t GetHeight();

protected:
	void *windowHandle;
	int32_t width;
	int32_t height;
};
And I also have this class EGLDisplayWindow which will inherit ApplicationWindow
class EGLDisplayWindow : public ApplicationWindow
{
public:
	EGLDisplayWindow();
	~EGLDisplayWindow();
	void CreateWindow(void *windowHandle);

private:
	void CreateAndroidWindow();
	void CreateIOSWindow();
	void DestroyWindow();


};

As you can see ApplicationWindow has a private variable called windowHandle and a method called GetWindow()
When a EGLDisplayWindow is created my idea is to create the proper window needed by each application. Then store that "window" into the windowHandle variable, where it can be accessed using the GetWindow() method

Is this a bad idea (including the void pointer part)? Is there a better solution?
Advertisement

The android/ios names suggest to me that this is for different window types on different platforms? Presumably you need to build different binaries for different platforms, so why not use #ifdef directives to control this instead (I mean... how would you implement CreateIOSWindow on android anyway?)

The android/ios names suggest to me that this is for different window types on different platforms? Presumably you need to build different binaries for different platforms, so why not use #ifdef directives to control this instead (I mean... how would you implement CreateIOSWindow on android anyway?)

Yeah, inheritance is the wrong mechanism here.

Depending on the complexity, you could also just have different cpp files for each platform. So you'd end up with
- ApplicationWindow.h // define platform neutral interface
- ApplicationWindow_Common.cpp // shared implementation
- ApplicationWindow_IOS.cpp // iOS specific code
- ApplicationWindow_Android.cpp // android specific code
And so on. Then just build the files you need on each platform
if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight

Is this a bad idea (including the void pointer part)? Is there a better solution?

Well, you're sort of on the right track (for example, on Linux, where you could have an X11 window, a Wayland window, a Mir window, all in the same binary). That's the whole thing about native EGL windows, and the solution is either a void* or something aliased to a void*.

Your code above, however, is not using a void* but trying to instantiate a void. That's not gonna work.

Stephen M. Webb
Professional Free Software Developer

The protected keyword in C++ is ugly. It's usually a good hint there's a better design hiding somewhere. That's off topic though :)

Yes, if you have to pass internal structures through public code, then void pointers are a common solution.
Personally, I like to make my own opaque pointer types for better self-documentation.
E.g.
struct InternalWindow; // doesn't actually exist / is never really used, except as a pointer type.
....
  virtual InternalWindow* GetWindow() const { return (InternalWindow*)m_Window;
  EglWindow* m_Window;
It's basically the same as the void pointer approach, but with a kind of comment forcible attached to all the opaque pointers :)

It's basically the same as the void pointer approach, but with a kind of comment forcible attached to all the opaque pointers


It also gives you compile-time type checks to make sure you aren't accidentally passing something that's not an InternalWindow to a function that wants an InternalWindow.

Even back in the OP's use case, using opaque pointers further means that you can't make mistake as easily. &foo automatically converts to void* but not to ArbitraryType*. Even if you want to pass in pointers to arbitrary types, you almost certainly want to require the user to do so very very explicitly, so using opaque (undefined) pointers in place of void* makes your interface harder to accidentally misuse, which is always good. At least use those kind of opaque pointers in the public interface (using void* in the internals is fine).

Sean Middleditch – Game Systems Engineer – Join my team!

The android/ios names suggest to me that this is for different window types on different platforms? Presumably you need to build different binaries for different platforms, so why not use #ifdef directives to control this instead (I mean... how would you implement CreateIOSWindow on android anyway?)


Sorry I never fully explained this, but yes you are correct it is for creating different windows on different platforms.
Basically my idea was to call CreateWindow(void *windowHandle) and then the guts of this method would look like:

void EGLDisplayWindow::CreateWindow(void * windowHandle)
{
     this.windowHandle = windowHandle;

#if defined(_ANDROID_)
   
    CreateAndroidWindow(this.windowHandle);

#endif

//Other platforms that need EGL Windows

}

Your code above, however, is not using a void* but trying to instantiate a void. That's not gonna work.


Yep! It was a typo. I fixed it up top


The protected keyword in C++ is ugly. It's usually a good hint there's a better design hiding somewhere. That's off topic though smile.png


Why is protected bad in C++? How else do you hide data members from everyone and only expose them to the inherited classes?

Personally, I like to make my own opaque pointer types for better self-documentation.


To make an opaque pointer is it literally only a empty struct?

To make an opaque pointer is it literally only a empty struct?

It's not even an empty struct - it's a struct that's only ever been forward-declared (so if you ever tried to actually create one, you'd get a conpuler error).

Why is protected bad in C++? How else do you hide data members from everyone and only expose them to the inherited classes?

Generally, you don't - that's what's bad about protected.
It's not a black and white rule, but a soft guideline, or a "code smell".
Architectures based on implementation-inheritance (as opposed to interface-inheritance often break a lot of OO design priciples).

Why is protected bad in C++? How else do you hide data members from everyone and only expose them to the inherited classes?


Because data members should never be protected, and few functions should use it. It does have some uses, but protected is rarely useful in C++, and most code can get away with never using it.

Herb Sutter (the chair of the C++ language committee basically for the past 20 years) has written several articles on it. I've included some links to some of the writing, there are probably better write-ups.

Basically it looks like this:



PUBLIC:
* Interface functions should be public. (The public functions should not be virtual. A surprisingly huge amount of bad code uses public virtual functions to achieve it's problems. When you can replace both the interface and behavior you can subvert things easily, both to be evil and as an accidental source of bugs.)
* Data members of POD classes/structs that serve as data bundles rather than logical classes.
* Base class destructor that is virtual and requires polymorphic destruction.

PROTECTED:
* Non-virtual utility functions to be used by child classes that relies on other member data in this base class. (Rare situation)
* Base class destructor that is non-virtual. (Rare situation)

* Virtual functions to be used by base classes that cannot be private because base classes must call them explicitly. (Design smell and source of bugs. Virtual functions are customizations that are an implementation detail. Why do you need to explicitly call a parent's customization? How do you know that customization will never change? If you have a tendency to do this, will you retrofit all other classes when it becomes necessary for others to use it? Instead put that into a protected utility function so all descendants can use it. )


PRIVATE:
* All data members, unless this is a data-only bundle.
* Any function not part of the publicly visible interface. This includes utility functions and virtual functions.

The vast majority of all code belongs in the public and private arena. There are occasional uses of protected, but they are rarely encountered.

This topic is closed to new replies.

Advertisement