Allegro.cc - Online Community

Allegro.cc Forums » Game Design & Concepts » Templated inheritance vs normal inheritance

This thread is locked; no one can reply to it. rss feed Print
Templated inheritance vs normal inheritance
J-Gamer
Member #12,491
January 2011
avatar

In my game, I have a class Drawable, which is just an interface class with a few pure virtual functions. Derived from that I have an SfmlDrawable, which is never directly used, but the classes derived from SfmlDrawable are. To save myself the hassle of defining a new class for each drawable I wanted to have, I opted to do it this way:

#SelectExpand
1template<class Follow, class Data> 2class SfmlDrawable : public Drawable { 3public: 4 SfmlDrawable(Follow* e, std::shared_ptr<SfData> env) : entity(e), graphics_env(env) {init();}; 5 void draw() {}; //Do nothing since we don't know what to draw. 6 ... //Other functions 7private: 8 void init() {}; //Initialize extra. 9 std::shared_ptr<SfData> graphics_env; //The graphical environment 10 Data extra; //The extra data needed to be able to draw. 11 Follow* entity; //The entity this class represents. 12} 13 14typedef SfmlDrawable<Player, sf::CircleShape> SfmlPlayer; 15template<> void SfmlPlayer::draw(); //override the draw method 16template<> void SfmlPlayer::init(); //override init to set the player color. 17... //Override every function that needs overriding. 18 19... //Do so for every drawable.

And have the implementation of the overridden functions in a .cpp file.
Any arguments against this or is this acceptable?

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

I don't know why you're doing it like this with templates. I read this earlier but I was a little stupified. It's not at all like what I have :

#SelectExpand
1 2 3/* 4 * 5 * _______ ___ ____ __ _______ _______ 6 * /\ ____\ /| \ / __\ /\ \ /\ ____\ /\ ____\ 7 * \ \ ___/_ || _ \ | /__/____\ \ \ \ \ ___/_ \ \ _____ 8 * \ \ ____\ || |_\ \ |\ \ /_ _\\ \ \ \ \ ____\ \ _____ \ 9 * \ \ ___/_ || ___ \ \ \ \//\ / \ \ ____\ \ ___/_ / ____\ \ 10 * \ ______\||_|__/_\ \ \ _/ | \ _____\\ ______\ /______\ 11 * /______/|/_/ /_/ ______/ /_____/ /______/ /______/ 12 * 13 * 14 * EAGLE 5 15 * Edgar's Agile Gui Library and Extensions 16 * 17 * Copyright 2009-2013+ by Edgar Reynaldo 18 * 19 * See EagleLicense.txt for allowed uses of this library. 20 * 21 */ 22 23 24 25#ifndef EagleGraphics_HPP 26#define EagleGraphics_HPP 27 28#include "Eagle5/EagleColor.hpp" 29#include "Eagle5/EagleEvents.hpp" 30#include "Eagle5/EagleImage.hpp" 31#include "Eagle5/EagleFont.hpp" 32#include "Eagle5/EagleContainer.hpp" 33 34 35 36 37enum EAGLE_DISPLAY_FLAGS { 38 EAGLE_WINDOWED = 1 << 0, 39 EAGLE_FULLSCREEN = 1 << 1, 40 EAGLE_OPENGL = 1 << 2, 41 EAGLE_DIRECT3D_INTERNAL = 1 << 3, 42 EAGLE_RESIZABLE = 1 << 4, 43 EAGLE_NOFRAME = 1 << 5, 44 EAGLE_GENERATE_EXPOSE_EVENTS = 1 << 6, 45 EAGLE_OPENGL_3_0 = 1 << 7, 46 EAGLE_OPENGL_FORWARD_COMPATIBLE = 1 << 8, 47 EAGLE_FULLSCREEN_WINDOW = 1 << 9, 48 EAGLE_MINIMIZED = 1 << 10, 49 EAGLE_USE_PROGRAMMABLE_PIPELINE = 1 << 11 50}; 51 52 53enum IMAGE_DRAWING_FLAGS { 54 DRAW_NORMAL = 0, 55 DRAW_HFLIP = 1, 56 DRAW_VFLIP = 2, 57 DRAW_HVFLIP = 3 58}; 59 60 61 62 63 64class REGION_INFO { 65 66public : 67 REGION_INFO(); 68 REGION_INFO(float srcx , float srcy , float srcw , float srch); 69 70 float sx; 71 float sy; 72 float sw; 73 float sh; 74}; 75 76 77 78class SCALE_INFO { 79 80public : 81 SCALE_INFO(); 82 SCALE_INFO(float xscale , float yscale); 83 84 float x; 85 float y; 86}; 87 88 89 90class RESIZE_INFO { 91 92public : 93 RESIZE_INFO(); 94 RESIZE_INFO(float destx , float desty , float destw , float desth); 95 96 float dx; 97 float dy; 98 float dw; 99 float dh; 100 101};// destination rectangle 102 103 104 105class ROTATE_INFO { 106 107public : 108 ROTATE_INFO(); 109 ROTATE_INFO(float pivotx , float pivoty , float destx , float desty , float theta); 110 111 float cx; 112 float cy; 113 float dx; 114 float dy; 115 float angle; 116}; 117 118 119 120class EagleDrawingInfo { 121 122private : 123 void CheckUse(); 124 125public : 126 127 EagleDrawingInfo(); 128 EagleDrawingInfo(float destx , float desty); 129 130 bool use_any; 131 bool use_region; 132 bool use_scale; 133 bool use_resize;// if false, destination point will be used, which is default 134 bool use_rotate; 135 bool use_tint; 136 137 float dx; 138 float dy; 139 140 REGION_INFO region;// sx sy sw sh 141 SCALE_INFO scale;// x y 142 RESIZE_INFO resize;// dx dy dw dh 143 ROTATE_INFO rotate;// cx cy dx dy angle 144 EagleColor tint;// r g b a 145 int flags;// DRAW_HFLIP DRAW_VFLIP DRAW_HVFLIP 146 147 void SetDest(float x , float y);// overrides resize, scale, and rotate 148 void SetRegion(REGION_INFO r); 149 void SetScale(SCALE_INFO s);// only used with rotate 150 void SetResize(RESIZE_INFO r);// overrides scale and rotate, latest setting wins 151 void SetRotate(ROTATE_INFO r);// overrides resize 152 void SetTintColor(EagleColor c); 153 void SetFlags(int f); 154 155 156 void ClearSettings(); 157}; 158 159 160 161 162class EagleGraphicsContext { 163 164protected : 165 int scrw; 166 int scrh; 167 168 EagleImage* backbuffer; 169 EagleImage* drawing_target; 170 171 PointerManager<EagleImage> images; 172 PointerManager<EagleFont> fonts; 173 174 175 176 177public : 178 EagleGraphicsContext(); 179 180 virtual ~EagleGraphicsContext() {} 181 182 // creation/destruction 183 virtual bool Create(int width , int height , int flags)=0; 184 virtual bool Valid()=0; 185 virtual void Destroy()=0; 186 187 // clears target bitmap 188 virtual void Clear(EagleColor c)=0; 189 190 // basic drawing operations 191 virtual void PutPixel(int x , int y , EagleColor c)=0; 192 virtual void DrawRectangle(int x , int y , int w , int h , int thickness , EagleColor c)=0; 193 virtual void DrawFilledRectangle(int x , int y , int w , int h , EagleColor c)=0; 194 virtual void DrawCircle(int cx , int cy , int radius , int thickness , EagleColor c)=0; 195 virtual void DrawFilledCircle(int cx , int cy , int radius , EagleColor c)=0; 196 virtual void DrawTriangle(int x1 , int y1 , int x2 , int y2 , int x3 , int y3 , int thickness , EagleColor c)=0; 197 virtual void DrawFilledTriangle(int x1 , int y1 , int x2 , int y2 , int x3 , int y3 , EagleColor c)=0; 198 199 // precise drawing operations 200 virtual void PutPixel(float x , float y , EagleColor c)=0; 201 virtual void DrawRectangle(float x , float y , float w , float h , float thickness , EagleColor c)=0; 202 virtual void DrawFilledRectangle(float x , float y , float w , float h , EagleColor c)=0; 203 virtual void DrawCircle(float cx , float cy , float radius , float thickness , EagleColor c)=0; 204 virtual void DrawFilledCircle(float cx , float cy , float radius , EagleColor c)=0; 205 virtual void DrawTriangle(float x1 , float y1 , float x2 , float y2 , float x3 , float y3 , float thickness , EagleColor c)=0; 206 virtual void DrawFilledTriangle(float x1 , float y1 , float x2 , float y2 , float x3 , float y3 , EagleColor c)=0; 207 208 // image drawing operations 209 virtual void Draw(EagleImage* src , float x , float y , int flags = DRAW_NORMAL)=0; 210// virtual void Draw(EagleImage* src , EagleDrawingInfo info = EagleDrawingInfo())=0; 211 212 // getters 213 virtual EagleImage* GetBackBuffer()=0; 214 virtual EagleImage* GetScreen()=0; 215 virtual EagleImage* GetDrawingTarget()=0; 216 217 // utilities 218 virtual void FlipDisplay()=0; 219 virtual void HoldDrawing()=0; 220 virtual void ReleaseDrawing()=0; 221 virtual void SetDrawingTarget(EagleImage* dest)=0; 222 void DrawToBackBuffer(); 223 224 // image creation / loading / sub division 225 virtual EagleImage* CreateImage(int width , int height , IMAGE_TYPE type)=0; 226 virtual EagleImage* LoadImage(std::string file , IMAGE_TYPE type)=0; 227 virtual EagleImage* CreateSubImage(EagleImage* parent , int x , int y , int width , int height)=0; 228 229 // font loading 230 virtual EagleFont* LoadFont(std::string file , int height , int flags , IMAGE_TYPE type)=0; 231 232 // event handler registration 233 virtual void RegisterDisplayInput(EagleEventHandler* queue)=0; 234 235}; 236 237 238#endif // EagleGraphics_HPP

Can you explain why you need the template, and can't simply derive new classes with out it?

l j
Member #10,584
January 2009
avatar

I've seen people use templates as a way to get 'compile time polymorphism'.
To reduce overhead from virtual calls when they can all be easily resolved at compile time. But I've never seen anybody use this thing. I don't even know what I'm looking at honestly.

J-Gamer
Member #12,491
January 2011
avatar

I chose for a template implementation for two reasons:

  1. All classes except an SfmlDrawableFactory uses Drawable*'s. So I never need an instance of SfmlDrawable anyway.

  2. Like I said in the OP, I don't want to need to define a new class when it's constructor will be exactly the same as the one from SfmlDrawable. All drawables will have enough data to fully initialize themselves in their init() method. Actually: this "reason" to do it just comes from pure lazyness on my part ;)

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

gnolam
Member #2,030
March 2002
avatar

OT:

Quote:
/*
 *     _______       ___       ____      __       _______       _______
 *    /\  ____\    /|   \     /  __\    /\ \     /\  ____\     /\  ____\
 *    \ \ ___/_   ||  _ \   |  /__/____\ \ \    \ \ ___/_    \ \ _____
 *     \ \  ____\  || |_\ \  |\ \ /_  _\\ \ \    \ \  ____\    \ _____ \
 *      \ \ ___/_ ||  ___ \ \ \ //\ / \ \ ____\ \ ___/_    / ____\ \
 *       \ ______\||_|__/_\ \ \ _/ |   \ _____\\ ______\     /______\
 *        /______/|/_/  /_/  ______/    /_____/ /______/     /______/
*/

That is some seriously mangled ASCII art... :)

--
Move to the Democratic People's Republic of Vivendi Universal (formerly known as Sweden) - officially democracy- and privacy-free since 2008-06-18!

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

It's Matthew's code parser, cuz it looks fine here in my editor. :P

As for OT, I still don't get it. What classes will derive from SFML drawable? Why? Just to inherit a draw and init method? That seems kinda silly. Are these gonna be full objects or what? What are these things that you're making a class for?

J-Gamer
Member #12,491
January 2011
avatar

As for OT, I still don't get it. What classes will derive from SFML drawable? Why? Just to inherit a draw and init method? That seems kinda silly. Are these gonna be full objects or what? What are these things that you're making a class for?

Quote:

typedef SfmlDrawable<Player, sf::CircleShape> SfmlPlayer;

This is how I make a new class. Player is a class from my game, and is an entity that runs around on the game world. All SfmlPlayer needs to be able to draw itself is a circle, which is initialized in the overridden init method. That is as far as "inheritance" goes here. SfmlPlayer, SfmlZombie, SfmlBullet,... actually are SfmlDrawable specializations.
The other option is to do this:

#SelectExpand
1class SfmlDrawable : public Drawable { 2public: 3 SfmlDrawable(std::shared_ptr<SfData> env) : graphics_env(env) {}; 4 virtual void draw() {}; 5 ... //Other functions, all virtual 6protected: 7 std::shared_ptr<SfData> graphics_env; 8} 9 10class SfmlPlayer : public SfmlDrawable { 11public: 12 SfmlPlayer(std::shared_ptr<SfData> env, Player* p) : SfmlDrawable(env), p(p) { 13 //Contents of init-method to initialize our shape. 14 } 15 void draw(); 16 //Name other functions that need to be overridden 17private: 18 sf::CircleShape shape; 19 Player* p; 20}

And write those last 10+ lines of code for SfmlZombie, SfmlBullet,...
My method comes from pure lazyness not wanting to write quasi-identical code for each drawable class. It's just more concise(ie: It only costs me three lines(+ implementation of those two functions) to add SfmlPlayer).
Also, as I said, I never need the fact that they are a specialization of SfmlDrawable, only that they derive from Drawable.

Templates are code generators, and since all SfmlDrawable classes have the same class layout, it's easier to automate the code generation.

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Go to: