#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2014
    Posts
    18
    Rep Power
    0

    Question Multiple Definition Errors when compiling


    Hello Everyone,

    Just for a bit of fun (and to brush up on my C++), I am in the process of developing a small game using the Allegro game library. I'm doing my development on Linux and so far things are going OK.

    Anyway, I wanted a fancy UI system for the game so I decided to develop a basic one from scratch. In the process of doing this, I wrote a base class called 'AlControl' which has most of the general features of the UI controls.
    I then wrote other classes which inherit from this (e.g. AlButton, AlMenu, AlLabel etc).

    The problem comes when I try and build everything. I can compile each of the UI files into object files without a problem but when I then try and build everything into a single binary I get errors such as:
    Code:
    error: previous definition of ‘class alui::AlControl’
    The errors are easy to understand and I can work around the problem by first compiling the object files and then commenting out the shared header before compiling the rest of the program but this is very inconvenient and this isn't a very scalable solution.

    AlButton looks something like this:
    Code:
    #include <allegro5/allegro.h>
    #include <string>
    
    #include "Image.hpp"
    #include "Coord.hpp"
    #include "Color.hpp"
    
    #include "Control.hpp"            /* <-- This include is added for each control, hence the 'previous definition' error */
    
    namespace alui {
        class AlButton : public AlControl {
            private:
                
            public:
                AlButton(): AlControl() {}
    	        
    	        AlButton(std::string fpath) : AlControl(fpath) {
    	            
    	        }
    	        
    	        AlButton(const char * fpath) : AlControl(fpath) {
    	            
    	        }
        };
    };
    AlLabel looks something like this:
    Code:
    #include <allegro5/allegro.h>
    #include <allegro5/allegro_font.h>
    #include <allegro5/allegro_ttf.h>
    #include <string>
    
    #include "Image.hpp"
    #include "Coord.hpp"
    #include "Color.hpp"
    
    //#include "ControlConstants.hpp"
    #include "Control.hpp"
    
    namespace alui {
        class AlLabel : public AlControl {
            private:
                std::string text;
                
            public:
                AlLabel(): AlControl() {}
    	        
    	        AlLabel(std::string text) : AlControl() {
    	            this->text = text;
    	        }
    	        
    	        AlLabel(const char * text) : AlControl() {
    	            this->text = std::string(text);
    	        }
    	        
    	        void render(int flags = ALLEGRO_ALIGN_CENTRE);
        };
    };
    This is the Makefile I'm currently using (I don't know much about the syntax for Makefiles so please bear with me):
    Code:
    CC=g++
    AR=ar
    PROJ_NAME=blaster
    CLIBS=`pkg-config --libs allegro-5` -lallegro_image -lallegro_acodec -lallegro_audio -lallegro_primitives -lallegro_font -lallegro_ttf
    CFLAGS=-std=c++11
    UI_LIBNAME=alui
    ALUI=./alui
    HLP=./helper
    SRC=./src
    INC=./include
    OUTDIR=./build
    
    ALUI_CPP := $(wildcard $(ALUI)/*.cpp)
    ALUI_OBJ := $(addprefix $(OUTDIR)/,$(notdir $(ALUI_CPP:.cpp=.o)))
    
    GAME_CPP := $(wildcard $(SRC)/*.cpp)
    GAME_OBJ := $(addprefix $(OUTDIR)/,$(notdir $(GAME_CPP:.cpp=.o)))
    
    HELP_CPP := $(wildcard $(HLP)/*.cpp)
    HELP_OBJ := $(addprefix $(OUTDIR)/,$(notdir $(HELP_CPP:.cpp=.o)))
    
    all: $(ALUI_OBJ) ui
    	cp "AppSettings.xml" "$(OUTDIR)/AppSettings.xml"
    	$(CC) $(GAME_CPP) $(HELP_CPP) $(OUTDIR)/*.o  -o $(OUTDIR)/$(PROJ_NAME) $(CFLAGS) $(CLIBS)
    
    clean:
    	rm -rf $(OUTDIR)/*
    
    ui: $(ALUI_OBJ)
    	$(AR) rvs $(OUTDIR)/$(UI_LIBNAME).a $^
    
    $(OUTDIR)/%.o: $(ALUI)/%.cpp
    	$(CC) -c -o $@ $< $(CFLAGS) $(CLIBS)
    How exactly should I change my code and my makefile so that I don't have to comment out the shared include after building the object files? As it stands at the moment, I must first run 'make ui' and then when that completes, I need to comment out the 'AlControl.hpp' line from AlMenu.hpp, AlButton.hpp, AlMenu.hpp etc and then run 'make all' to complete the build.

    In an ideal world, I would also like to build the UI part into a shared library (but that is a later step).
  2. #2
  3. Contributed User
    Devshed Specialist (4000 - 4499 posts)

    Join Date
    Jun 2005
    Posts
    4,462
    Rep Power
    1874
    You need to post what's in #include "Control.hpp"

    Somehow, you've ended up with definitions in there as opposed to just declarations.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2014
    Posts
    18
    Rep Power
    0
    Hello Salem,

    Thanks for your response. This is what I've currently got in Control.hpp:

    Code:
    #include <allegro5/allegro.h>
    #include <allegro5/allegro_primitives.h>
    
    #include <string>
    #include <cmath>
    #include <map>
    #include <algorithm>
    #include <functional>
    
    #include "Image.hpp"
    #include "Coord.hpp"
    #include "Color.hpp"
    
    #include "alui_control_constants.hpp"
    
    /* Define the default number of corners of the bounding box.
     * Might want more corner points for complicated convex or concave
     * primitives. */
    #define DEFAULT_CORNER_COUNT                 4
    
    namespace alui {
        
        // Base class for Allegro UI controls //
        class AlControl {
            private:
                //std::string name;
                bool is_active = false;
                bool is_hover = false;
                bool is_enabled = true;
                bool is_visible = true;
                
                AlImage * base_image = nullptr;
                AlImage * disabled_image = nullptr;
                std::map<EventType, AlImage *> control_images;    // Multiple images for multiple states (e.g. control might have hover or click states with different images //
                Color tint_color = {1.0f, 1.0f, 1.0f, 1.0f};      // Default 'tint' of the control - if image doesn't display etc //
                
                float scale_factors[2] = {1.0f, 1.0f};
                
                AlControl * parent = nullptr;
                std::vector<AlControl *> child_controls;
                std::multimap<EventType, std::function<void(ALLEGRO_EVENT)> > attached_functions;
                
            protected:
                float dimensions[2] = {0, 0};
                float angle = 0.0f;
                Coord centre = {0, 0};
                Coord corners[DEFAULT_CORNER_COUNT];                // Corners of object - Straight lines between corners is how bounding box is calculated //
                
                bool onControlEvent(int x, int y);
                void recalculateCoords();
                AlImage * getCurrentStateImage();
                
                void Initialise() {
                    for(int i=0; i<DEFAULT_CORNER_COUNT; i++)
                        this->corners[i] = Coord(0, 0);
                    this->centre = Coord(0, 0);
                };
                
            public:
                AlControl() { this->Initialise(); };
                AlControl(std::string fpath) {
                    this->Initialise();
                    this->loadBaseImage(fpath);
                }
                
                AlControl(const char * fpath) {
                    this->Initialise();
                    this->loadBaseImage(fpath);
                };
                
                ~AlControl() {
                    // Note: Deleting a control will also delete any child controls //
                    for(int i=0; i<this->child_controls.size(); i++)
                        delete this->child_controls[i];
                    this->child_controls.clear();
                    
                    this->parent->removeChild(this);
                    
                    for(std::map<EventType, AlImage *>::iterator it = this->control_images.begin(); it != this->control_images.end(); ++it)
                        delete this->control_images[it->first];
                    this->control_images.clear();
                };
                
                Coord getPosition();
                float getWidth();
                float getHeight();
                
                bool getVisibility();
                bool getEnabledState();
                Color getTint();
                
                AlImage * getBaseImage() { return this->base_image; }
                
                std::vector<AlControl *> getChildControls();
                AlControl * getParentControl();
                
                AlControl& addImageState(EventType evt, AlImage * image);
                AlControl& addImageState(EventType evt, std::string img_path);
                AlControl& removeImageState(EventType evt);
                
                AlControl& setPosition(unsigned int x, unsigned int y, Positioning cpoint = Positioning::Absolute);    // Returns itself - allows method chaining //
                
                AlControl& setScaleFactors(float scale_x, float scale_y);
                AlControl& setWidth(float width);
                AlControl& setHeight(float height);
                AlControl& setTint(Color tint);
                
                AlControl& setVisibility(bool visibility);
                AlControl& setEnabled(bool enabled);
                AlControl& setAngle(float angle);
                
                AlControl& setParent(AlControl * ctrl) {
                    this->parent = ctrl;
                    return *this;
                }
                
                AlControl& removeParent() {
                    this->parent->removeChild(this);
                    this->parent = nullptr;
                    return *this;
                }
                
                AlControl& addChild(AlControl * ctrl) {
                    ctrl->setParent(this);
                    this->child_controls.push_back(ctrl);
                    return *this;
                };
                
                AlControl& removeChild(AlControl * ctrl) {
                    for(std::vector<AlControl *>::iterator it = this->child_controls.begin(); it != this->child_controls.end(); ++it) {
                        if( *it == ctrl )
                            this->child_controls.erase(it);
                    }
                    return *this;
                };
                
                AlControl& attachEvent(EventType evt, std::function<void(ALLEGRO_EVENT)> evt_func);
                AlControl& removeEvents(alui::EventType evt);
                AlControl& removeEvents();
                
                void notifyEvent(ALLEGRO_EVENT ev, AlControl * sender = nullptr, bool notify_children = true);
                
                void render(int flags = 0);
                void renderAll(int flags = 0);
                void renderChildren(int flags = 0);
                
                bool loadBaseImage(std::string fpath);
                bool loadBaseImage(const char * fpath);
                
                bool loadDisableImage(std::string fpath);
                bool loadDisableImage(const char * fpath);
        };
    };
    Update: I'm a total idiot - I forgot to add #include guards on my Control.hpp file.. It works fine if they are added. I can't believe I missed that.

    Comments on this post

    • salem agrees : Good job :)

IMN logo majestic logo threadwatch logo seochat tools logo