Templated inheritance vs normal inheritance
J-Gamer

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?

Edgar Reynaldo

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

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

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 ;)

gnolam

OT:

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

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

Edgar Reynaldo

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

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.

Thread #611756. Printed from Allegro.cc