Advertisement

Problem with variable arguments

Started by November 08, 2015 02:46 AM
29 comments, last by Aardvajk 9 years, 2 months ago

Whenever I try to compile this files then it results in this error:

||=== Build: Debug in Game(compiler: GNU GCC Compiler) ===|

menuhandler.cpp||In member function 'void Gui::GuiHandler<C>::addGuiElem(...)':|
menuhandler.cpp|42|error: expected primary-expression before '...' token|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 1 second(s)) ===|

menuhandler.hpp

#pragma once

namespace Gui
{
    class GuiBase
    {
        public:
            float PosX;
            float PosY;
            float Width;
            float Height;
            void SetPos(float newX, float newY);
            void Move(float newX, float newY, float rate);
            sf::Text textDisplay;
            sf::Image imgDisplay;
    };

    class Button : public GuiBase
    {
        public:
            Button(float x, float y, float width, float height, sf::String text, sf::Font& font, unsigned int textSize);
            bool isClicked();
            bool isMouseIn();
            void SetText(sf::String newText);
    };
    template <class C>
    class GuiHandler
    {
        public:
            std::vector<std::shared_ptr<C>> GuiElements;
            GuiHandler();
            void addGuiElem(...);
            void DrawElements(sf::RenderWindow& window);
    };
}

menuhandler.cpp


#include <SFML/Graphics.hpp>
#include <vector>
#include <memory>
#include "menuhandler.hpp"

void Gui::GuiBase::SetPos(float newX, float newY)
{
    PosX = newX;
    PosY = newY;
}

void Gui::GuiBase::Move(float newX, float newY, float rate)
{

}

Gui::Button::Button(float x, float y, float width, float height, sf::String text, sf::Font& font, unsigned int textSize)
{
    PosX = x;
    PosY = y;
    Width = width;
    Height = height;
    textDisplay.setFont(font);
    textDisplay.setString(text);
    textDisplay.setCharacterSize(textSize);
    textDisplay.setPosition(x, y);
}

void Gui::Button::SetText(sf::String newText)
{
    textDisplay.setString(newText);
}

template <class C>
Gui::GuiHandler<C>::GuiHandler()
{
}

template <class C>
void Gui::GuiHandler<C>::addGuiElem(...)
{
    GuiElements.emplace_back(new C(...));
}

template <class C>
void Gui::GuiHandler<C>::DrawElements(sf::RenderWindow& window)
{
    for (auto i: GuiElements)
    {
        window.draw(i->textDisplay);
        //window.draw(i->imgDisplay);
    }
}

What am I doing wrong here? help please...

How about making a 10-line program that shows the problem? Do I really need to install SFML to try to reproduce the problem you are having?

EDIT: Anyway, this line seems plain wrong:
    GuiElements.emplace_back(new C(...));
What do you think that is doing?

Look up varargs and see some examples of usage.
Advertisement

Note that right now C++ has two language features for variadic arguments: the old C style variadic functions and variadic template function parameters. You are using the syntax for the former. However, you can't do argument forwarding with C-style variadic functions. You'll need variadic templates for that.

SiCrane mentioned variadic template parameters. Here's the syntax:


template<typename ...Args>
void Blah(const std::string &unrelatedA, int unrelatedB, Args&& ...args)
{
    Object object( std::forward<Args>(args)... );
}

SiCrane mentioned variadic template parameters. Here's the syntax:


template<typename ...Args>
void Blah(const std::string &unrelatedA, int unrelatedB, Args&& ...args)
{
    Object object( std::forward<Args>(args)... );
}

I think I'll try and do that, tho, how would I declare that in a header file? should I simply do:

void Blah(const std::string &unrelatedA, int unrelatedB, Args&& ...args);

Or should I do something like:

template<typename ...Args>
void Blah(const std::string &unrelatedA, int unrelatedB, Args&& ...args);

Ok, so I've made a test program:

test.cpp


#include <vector>
#include <memory>
#include <iostream>

class Base
{
    public:
        int MyValue;
        int getVal();
};

int Base::getVal()
{
    std::cout << "My value is: " << MyValue << std::endl;
}

class A : public Base
{
    public:
        A(int i);
};

A::A(int i)
{
    MyValue = i;
}

class handler
{
    public:
        std::vector<std::unique_ptr<Base>> objs;
        handler();
        template <class T, class... Args>
        void addObj(T, Args&&... args);
};

handler::handler() {}

template <typename T, typename... Args>
void handler::addObj(T, Args&&... args)
{
    objs.push_back(std::unique_ptr<T>(new T(args...)));
}

int main()
{
    handler handlerA;
    handlerA.addObj(A, 1);
}

I get an error tho:

test.cpp||In function 'int main()':|
test.cpp|48|error: expected primary-expression before ',' token|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

I don't know what's causing that error sad.png

Advertisement
You can't pass a type as an argument. Try this instead:

#include <vector>
#include <memory>
#include <iostream>

class Base
{
    public:
        int MyValue;
        int getVal();
};

int Base::getVal()
{
    std::cout << "My value is: " << MyValue << std::endl;
}

class A : public Base
{
    public:
        A(int i);
};

A::A(int i)
{
    MyValue = i;
}

class handler
{
    public:
        std::vector<std::unique_ptr<Base>> objs;
        handler();
        template <class T, class... Args>
        void addObj(Args&&... args);
};

handler::handler() {}

template <typename T, typename... Args>
void handler::addObj(Args&&... args)
{
    objs.push_back(std::unique_ptr<T>(new T(args...)));
}

int main()
{
    handler handlerA;
    handlerA.addObj<A>(1);
}

I think I'll try and do that, tho, how would I declare that in a header file? should I simply do:

Templated functions usually have to be in header files. The entire declaration (not just the definition) of the function needs to be visible to the caller of the function, so it can fill out the template to generate the code (this is an inaccurate oversimplification useful when learning templates).

Remember, templates aren't like regular functions, because the code for the function has to get generated only after you pass it parameter types. They get compiled into regular functions though.

MyFunc<int>() and MyFunc<float>() generate two different functions using the same function template.

Ok, so I've spent a little time on trying to improve it but it always ends up with the error:

error: statement cannot resolve address of overloaded function

Where the error happens:


guiHandler.addObj<Gui::Button, 10.f, 20.f, 25.f, 25.f, "Button_1", &MainFont, 30u>;

Some code:

part of menuhandler.hpp:


class Button : public GuiBase
{
    public:
        Button(float x, float y, float width, float height, sf::String text, sf::Font& font, unsigned int textSize);
        bool isClicked();
        bool isMouseIn();
        void SetText(sf::String newText);
};

class GuiHandler
{
    public:
        std::vector<std::unique_ptr<GuiBase>> container;
        GuiHandler();
        template <class C, typename... Args>
        void addObj();
        void drawElements(sf::RenderWindow& window);
}; 

part of menuhandler.cpp:


template <class C, typename... Args>
void Gui::GuiHandler::addObj()
{
    container.push_back(std::unique_ptr<C>(new C(args...)));
}

I really have no idea on what causes that error to show up wacko.png. Help please.

You appear to be passing everything as a template parameter now, including things that make absolutely no sense. On top of that you don't even call addObj then. I would strongly recommend to take a huge step back and forget about parameter packs for now. You seriously need a lot more practice and experience with basic C++ and basic templates.

This topic is closed to new replies.

Advertisement