I'm new and I'm learning c++ and a bit of the SFML.
To test what my studies I started consulting the "SFML game development" book, from wich I got the ResourceHolder in the code.
The problem is that when I try to implement it the code does compile just fine, but it doesn't execute. After many hours of test and various problems I've arrived to the point where i think (Hope) lays the last error.
The problem should be the initialization of the first class, that doesn't initialize because the ResourceHolder that is contained causes problems.
main.cpp
#include "Game.h"
int main(){
std::cout << "enter main" << std::endl;`
Game game;
std::cout << "after initialization" << std::endl;
}
Game.h
#include "ResourceHolder.h
class Game {
public:
typedef ResourceHolder<sf::Texture, ID> TextureHolder;
Game();
private:
ResourceHolder<sf::Texture, ID> media;
};
Game.cpp
#include "Game.h"
Game::Game()
{
std::cout << "initializing game" << std::endl;
}
ResourceHolder.h
#include <map>
#include <string>
#include <memory>
#include <stdexcept>
#include <cassert>
#include <sfml/Graphics.hpp>
#include <iostream>
enum class ID {
Background,
Player,
MAX_ENUM,
};
template <typename Resource, typename Identifier>
class ResourceHolder {
public:
typedef ResourceHolder<sf::Texture, ID> TextureHolder;
ResourceHolder()
{
TextureHolder texture; //todo <--My guess is here starts the trouble
std::cout << "here" << std::endl;
texture.load(ID::Background, "Media/Image1.png");
texture.load(ID::Player, "Media/Image2.png");
}
void load(Identifier id, const std::string& filename);
Resource& get(Identifier id);
const Resource& get(Identifier id) const;
private:
std::map<ID, std::unique_ptr<sf::Texture>> mResourceMap;
//The IDE flags this as non-used ^^^
};
#include "ResourceHolder.inl"
ResourceHolder.inl
template <typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::load(Identifier id, const std::string& filename)
{
std::unique_ptr<sf::Texture> resource(new Resource());
if (!resource->loadFromFile(filename))
throw std::runtime_error("ResourceHolder::load -Failed to load " + filename);
auto inserted = mResourceMap.insert(std::make_pair(id, std::move(resource)));
assert(inserted.second);
}
template <typename Resource, typename Identifier>
Resource& ResourceHolder<Resource, Identifier>::get(Identifier id)
{
auto found = mResourceMap.find(id);
assert(found != mResourceMap.end());
return *found->second;
}
template <typename Resource, typename Identifier>
const Resource& ResourceHolder<Resource, Identifier>::get(Identifier id) const
{
auto found = mResourceMap.find(id);
assert(found != mResourceMap.end());
return *found->second;
}
Thanks in advance!And pls, tell me if I can improve the question in any way, I am scared this could be too verbose even as tho I stripped all non necessary functions and variables.
Related
I've been trying to implement the Self Registering Factory pattern in my project and, after trying a bunch of ways to do it, I've settled for this solution.
Unfortunately, I've stumbled upon a problem where my code isn't compiling because my base class doesn't have any arguments to pass. There is a comment in the provided link detailing this exact issue but I must say that I don't understand why it doesn't work and how to make it work if possible.
Here is the error I get when compiling:
could not convert ‘std::make_unique(_Args&& ...) [with _Tp = ObjectA; _Args = {}; typename std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<ObjectA, std::default_delete<ObjectA> >]()’ from ‘unique_ptr<ObjectA,default_delete<ObjectA>>’ to ‘unique_ptr<BaseClass,default_delete<BaseClass>>’
40 | return std::make_unique<T>(std::forward<Args>(args)...);
| ^
| |
| unique_ptr<ObjectA,default_delete<ObjectA>>
For the sake of clarity, I'll post the code and example of classes I'm trying to implement with it.
selfregisteringfactory.h
#include <memory>
#include <unordered_map>
#include <string>
#include <cstdlib>
#include <cxxabi.h>
std::string demangle(const char *name) {
int status = -4;
std::unique_ptr<char, void (*)(void*)> res{
abi::__cxa_demangle(name, NULL, NULL, &status), free};
return (status == 0) ? res.get() : name;
}
template<class Base, class... Args>
class SelfRegisteringFactory {
public:
template<class ... T>
static std::unique_ptr<Base> make(const std::string &name, T&&... args) {
return data().at(name)(std::forward<T>(args)...);
}
friend Base;
template <class T>
class Registrar : Base {
friend T;
static bool registerT() {
const auto name = demangle(typeid(T).name());
SelfRegisteringFactory::data()[name] = [](Args... args) -> std::unique_ptr<Base> {
return std::make_unique<T>(std::forward<Args>(args)...);
};
return true;
}
static bool registered;
private:
Registrar() : Base(Key{}) { (void)registered;};
};
private:
class Key {
Key(){};
template <class T> friend class Registrar;
};
using FuncType = std::unique_ptr<Base> (*)(Args...);
SelfRegisteringFactory() = default;
static std::unordered_map<std::string, FuncType> &data(){
static std::unordered_map<std::string, FuncType> s;
return s;
}
};
template <class Base, class... Args>
template <class T>
bool SelfRegisteringFactory<Base, Args...>::Registrar<T>::registered =
SelfRegisteringFactory<Base, Args...>::Registrar<T>::registerT();
baseclass.h
#include "selfregisteringfactory.h"
#include <string>
class BaseClass : public SelfRegisteringFactory<BaseClass>{
public:
BaseClass(Key){};
virtual ~BaseClass() = default;
virtual void process() = 0;
virtual std::string getType() = 0;
};
objecta.h
#include "baseclass.h"
class ObjectA: public BaseClass::Registrar<ObjectA>{
public:
ObjectA();
virtual ~ObjectA() = default;
virtual void process();
virtual std::string getType();
};
objecta.cpp
#include "objecta.h"
#include <iostream>
ObjectA::ObjectA(){
}
void ObjectA::process(){
std::cout << "This is a process." << std::endl;
}
std::string ObjectA::getType(){
return "ObjectA";
}
Update
As #AlanBirtles pointed out, I've missed the public when writting the Registrar class. The code compiles but when I test it with my unit tests it doesn't seems to register ObjectA. I'm getting the out-of-range exception from the .at().
Here's what my test file looks like:
selfregisteringfactory.test.cpp
#include "catch2/catch.hpp"
#include "catch/fakeit.hpp"
#include "baseclass.h"
using namespace fakeit;
TEST_CASE( "TEST SelfRegisteringFactory class." )
{
SECTION("Test if adding an OjbjectA to the factory is possible.")
{
auto objA = DeviceCommunication::make("ObjectA");
REQUIRE(objA ->getType() == "ObjectA");
}
}
So I am creating a Stack class for an assignment in C++. The core of the assignment is to familiarize us with templates. I have read my book over and over and looked question after question on here.
I need to have my Stack class be able to be constructed by
Stack s2;
but I get an error when I compile my test.cpp and can only compile when i construct as
Stack<T> s1;
where T is a std::string, int, etc. How do build my Stack so I can use both constructors?
Stack.cpp
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string>
using namespace std;
template<typename T>
class Stack {
public:
Stack();
void Push(T val);
T Pop();
void Print();
private:
vector<T> vecT;
};
template <typename T>
Stack<T>::Stack() { }
template <typename T>
void Stack<T>::Push(T val) { vecT.push_back(val); }
template <typename T>
T Stack<T>::Pop() { vecT.pop_back(); }
template <typename T>
void Stack<T>::Print() {
cout << "[ ";
for(int i=0; i<vecT.size(); i++) {
cout << vecT[i] << " ";
}
cout << "]";
}
test.cpp
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string>
#include "Stack.cpp"
using namespace std;
int main() {
Stack<string> s1;
s1.Push("values1");
s1.Push("values2");
s1.Print();
Stack s2;
s2.Push("values1");
s2.Push("values2");
s2.Print();
}
What about a default template parameter?
template<typename T = std::string>
class Stack {
Anyway, with
Stack<T> s1;
and
Stack s2;
you're not using different constructors; you're using, in both cases, the same default (no arguments) constructor.
Instead of templating everything you could just use a stack of a variant type (since C++17, before use Boost.Variant). With the code below you can now push int, double, and std::string onto the stack. Furthermore, Pop() is missing a return statement. Also, pop_back() of vector returns nothing.
#include <iostream>
#include <vector>
#include <string>
#include <variant>
class Stack {
using Variant = std::variant<int,double,std::string>;
public:
Stack();
void Push(Variant val);
void Pop();
void Print();
private:
std::vector<Variant> vecT;
};
Stack::Stack() : vecT() {}
void Stack::Push(Variant val) { vecT.push_back(val); }
void Stack::Pop() { vecT.pop_back(); }
void Stack::Print() {
std::cout << "[ ";
for ( auto const& v : vecT )
std::visit([] (auto&& arg) { std::cout << arg << " "; }, v);
std::cout << "]\n";
}
int main() {
Stack s1;
s1.Push("values1");
s1.Push("values2");
s1.Print();
Stack s2;
s2.Push("values1");
s2.Push("values2");
s2.Print();
}
With Boost.Variant you get a C++98 compatible solution.
#include <iostream>
#include <vector>
#include <string>
#include <boost/variant.hpp>
class Stack {
typedef boost::variant<int,double,std::string> Variant;
typedef std::vector<Variant>::iterator Iterator;
std::vector<Variant> vecT;
struct visitor : public boost::static_visitor<void>
{
template < typename T >
void operator()(T const& arg) const { std::cout << arg << " "; }
};
public:
Stack();
void Push(Variant val);
void Pop();
void Print();
};
Stack::Stack() : vecT() {}
void Stack::Push(Variant val) { vecT.push_back(val); }
void Stack::Pop() { vecT.pop_back(); }
void Stack::Print() {
std::cout << "[ ";
for ( Iterator it = vecT.begin(); it != vecT.end(); ++it )
boost::apply_visitor( visitor(), *it );
std::cout << "]\n";
}
int main() {
Stack s1;
s1.Push("values1");
s1.Push("values2");
s1.Print();
Stack s2;
s2.Push("values1");
s2.Push("values2");
s2.Print();
}
You could use a default template parameter:
template<typename T = int>
class Stack {
Then you could construct using:
Stack<> s2;
Also, the constructors are identical, you aren't calling a different one each time, but rather the template argument differs.
I am aware that there is quite a few topics around this, but I just can't figure it out.
I am working for a Signal driven input manager for my game and since I need to control multiple players, I need to connect a member function of a PlayerController instance to my vector of signals.
Problem is, that there is not only going to be a PlayerController, but also a MenuController etc. So I will need to connect a member function of a MenuController to the signal aswell.
I am pretty sure I am close to the solution, but I just can't seem to figure it out.
Can someone help me with the registerEvent function signature and how to call the connect method.
InputManager.tcc
#include "boost/function.hpp"
template<class T>
void InputManager::registerEvent(SDL_Keycode key,KeyState state,boost::function<void ()> const& function)
{
auto &inputEvents = (state == KeyState::Up) ? m_keyUpEvents : m_keyDownEvents;
if(inputEvents.find(key) == inputEvents.end())
{
inputEvents.insert(std::make_pair(key, boost::signals2::signal<void()>()));
m_keyStates[key] = KeyState::Up;
}
inputEvents[key].connect(boost::bind(T::function, instance));
}
InputManager.hpp:
#ifndef SSB_INPUTMANAGER_HPP
#define SSB_INPUTMANAGER_HPP
#include <functional>
#include <algorithm>
#include <vector>
#include <map>
#include <SDL_keycode.h>
#include <SDL_events.h>
#include <boost/signals2.hpp>
enum KeyState{
Down,
Up
};
class InputManager
{
public:
InputManager();
template<class T>
void registerEvent(SDL_Keycode key,KeyState state, boost::function<void ()> const& function);
void pollEvent(SDL_Event event);
private:
std::map<SDL_Keycode, boost::signals2::signal<void ()>> m_keyDownEvents;
std::map<SDL_Keycode, boost::signals2::signal<void ()>> m_keyUpEvents;
std::map<SDL_Keycode, KeyState> m_keyStates;
};
#include "InputManager.tcc"
#endif //SSB_INPUTMANAGER_HPP
PlayerController.hpp
#ifndef SSB_PLAYERCONTROLLER_HPP
#define SSB_PLAYERCONTROLLER_HPP
class PlayerController
{
public:
void jump();
private:
Player m_player;
};
#endif //SSB_PLAYERCONTROLLER_HPP
Then I want to call in an initialization method somewhere in my game:
PlayerController playerController;
InputManager inputController;
inputController.registerEvent(SDLK_0, KeyState::Down, playerController.jump());
Both boost::function<> and std::function<> already do type erasure. This means that they "abstract" away any bound parameters.
The this* argument is really just that: a parameter. So, the same goes for it.
Live On Coliru (c++11)
#include <boost/function.hpp>
#include <boost/signals2.hpp>
#include <map>
#include <iostream>
enum SDL_Keycode { K_A, K_B, K_C, K_Up, K_Down, K_Right, K_Left, K_LCtrl, /*etc....*/ };
enum KeyState { Down, Up };
struct InputManager {
void registerEvent(SDL_Keycode key, KeyState state, boost::function<void()> const &function) {
auto& map = state == Up? m_keyUpEvents : m_keyDownEvents;
map[key].connect(function);
}
void poll_event() {
// hardcoded for demo
m_keyDownEvents[K_B]();
}
private:
std::map<SDL_Keycode, boost::signals2::signal<void()>> m_keyDownEvents;
std::map<SDL_Keycode, boost::signals2::signal<void()>> m_keyUpEvents;
std::map<SDL_Keycode, KeyState> m_keyStates;
};
struct Player {};
class PlayerController {
public:
void jump() {
std::cout << "Player jumped\n";
}
private:
Player m_player;
};
class CowController {
public:
void moo() {
std::cout << "Cow mooed\n";
}
};
int main() {
InputManager inputController;
PlayerController p;
inputController.poll_event(); // nothing
inputController.registerEvent(K_B, Down, [&] { p.jump(); });
inputController.poll_event(); // player jumps
CowController c;
inputController.registerEvent(K_B, Down, [&] { c.moo(); });
inputController.poll_event(); // player jumps, cow moos
}
Prints
Player jumped
Player jumped
Cow mooed
C++03
If you don't have lambdas, you can use Boost Bind (or std::tr1::bind):
inputController.registerEvent(K_B, Down, boost::bind(&PlayerController::jump, boost::ref(p)));
inputController.poll_event(); // player jumps
CowController c;
inputController.registerEvent(K_B, Down, boost::bind(&CowController::moo, boost::ref(c)));
See it Live On Coliru
I have the following three files, of which I cannot find the source of an error that it is producing:
Main.cpp
#include <SFML/Graphics.hpp>
#include <iostream>
#include "ResourceHolder.h"
namespace Textures
{
enum ID { Landscape, Airplane, Missile };
}
int main()
{
//...
try
{
ResourceHolder<sf::Texture, Textures::ID> textures;
textures.load(Textures::Airplane, "Airplane.png");
}
catch (std::runtime_error& e)
{
std::cout << "Exception: " << e.what() << std::endl;
}
//...
}
ResourceHolder.h
#pragma once
#include <map>
#include <string>
#include <memory>
#include <stdexcept>
#include <cassert>
template <typename Resource, typename Identifier>
class ResourceHolder
{
public:
void load(Identifier id, const std::string& fileName);
Resource& get(Identifier id);
const Resource& get(Identifier id) const;
private:
void insertResource(Identifier id, std::unique_ptr<Resource> resource);
std::map<Identifier, std::unique_ptr<Resource>> mResourceMap;
};
ResourceHolder.cpp
#include "ResourceHolder.h"
template <typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::load(Identifier id, const std::string& fileName)
{
//Create and load resource
std::unique_ptr<Resource> resource(new Resource());
if (!resource->loadFromFile(fileName)) {
throw std::runtime_error("ResourceHolder::load - Failed to load " + fileName);
}
//If loading was successful, insert resource to map
insertResource(id, std::move(resource));
}
template <typename Resource, typename Identifier>
Resource& ResourceHolder<Resource, Identifier>::get(Identifier id)
{
auto found = mResourcemap.find(id);
assert(found != mResourceMap.end());
return *found->second();
}
template <typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::insertResource(Identifier id, std::unique_ptr<Resource> resource)
{
//Insert and check success
auto inserted = mResourceMap.insert(std::make_pair(id, std::move(resource)));
assert(inserted.second);
}
If I were to remove the try-catch combination in main.cpp, the code compiles fine; However, if I leave it there it gives me an LNK2019 (Unresolved external symbol) Error.
What is the source of this error, and how would I fix it?
You can't define templates inside .cpp files. They have to be defined in the header so the compiler can see the implementation and generate the specific classes.
Here's a better question/answer on why it is so Why can templates only be implemented in the header file?.
EDIT: What's wrong in the get function
Two things.
First is this auto found = mResourcemap.find(id);. Your map name is incorrect, m should be upper case -> mResourceMap.
Then the line return *found->second();. The map iterator contains a pair, and the first and second members are not functions but data members. You should write return *found->second;.
I would advise you to understand the structures you're working with before using templates. The compile errors with templates are pretty messy and harder to read. Also you could make a separate test program and make a resource manager with no templates to understand your errors more easily, then build the template on top of your working resource manager.
With all the other answers providing you with enough information to why your code don't compile and might not be valid, this is a resource manager i wrote for SFML some time ago, might be of use to you:
HPP FILE:
#ifndef RESOURCEMANAGER_HPP
#define RESOURCEMANAGER_HPP
/************ INCLUDES ***********/
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "SFML/Graphics.hpp"
#include "SFML/Audio.hpp"
class ResourceManager
{
private:
std::map<std::string,std::unique_ptr<sf::Texture>> listImageContainer;
std::map<std::string,std::pair<std::unique_ptr<sf::SoundBuffer>,std::unique_ptr<sf::Sound>>> listSoundContainer;
std::map<std::string,std::unique_ptr<sf::Font>> listFontContainer;
public:
ResourceManager();
std::unique_ptr<sf::Sound>& LoadSound(const std::string);
std::unique_ptr<sf::Font>& LoadFont(const std::string);
std::unique_ptr<sf::Texture>& LoadImage(const std::string);
~ResourceManager();
};
#endif
CPP FILE:
#include "ResourceManager.hpp"
ResourceManager::ResourceManager()
{
}
std::unique_ptr<sf::Sound>& ResourceManager::LoadSound(const std::string _fileName)
{
if (listSoundContainer.find(_fileName) == listSoundContainer.end())
{
std::unique_ptr<sf::SoundBuffer> soundBuffer(new sf::SoundBuffer());
if (soundBuffer->loadFromFile("assets/sound/" + _fileName) != false)
{
std::unique_ptr<sf::Sound> sound(new sf::Sound(*soundBuffer));
listSoundContainer[_fileName] = std::make_pair(std::move(soundBuffer), std::move(sound));
return listSoundContainer[_fileName].second;
}
else
{
std::cerr << "Error loading sound..." << std::endl;
}
}
else
{
return listSoundContainer[_fileName].second;
}
}
std::unique_ptr<sf::Font>& ResourceManager::LoadFont(const std::string _fileName)
{
if (listFontContainer.find(_fileName) == listFontContainer.end())
{
std::unique_ptr<sf::Font> font(new sf::Font());
if (font->loadFromFile("assets/font/" + _fileName)!=false)
{
listFontContainer[_fileName] = std::move(font);
return listFontContainer[_fileName];
}
else
{
std::cerr << "Error loading font..." << std::endl;
}
}
else
{
return listFontContainer[_fileName];
}
}
std::unique_ptr<sf::Texture>& ResourceManager::LoadImage(const std::string _fileName)
{
if (listImageContainer.find(_fileName) == listImageContainer.end())
{
std::unique_ptr<sf::Texture> texture(new sf::Texture);
if (texture->loadFromFile("assets/image/" + _fileName)!=false)
{
listImageContainer[_fileName] = std::move(texture);
return listImageContainer[_fileName];
}
else
{
std::cerr << "Error loading image: " << _fileName << std::endl;
}
}
else
{
return listImageContainer[_fileName];
}
}
ResourceManager::~ResourceManager(){}
How to use:
ResourceManager resourceManager;
auto& sound = resourceManager.LoadSound("nice.wav");
auto& image = resourceManager.LoadImage("head.png");
auto& sound2 = resourceManager.LoadSound("nice.wav"); //<--- already loaded
sound.play();
etc...
I'm trying to wrap std::packaged_task inside another class in order to be used together with a task scheduler.
At the moment I got it all working except std::future support. To get std::future support I figured out I need to use std::packaged_task for the get_future() function that it provides.
I've been trying whole day all sorts of ways to get this to work, but I seem to be unable to properly declare and initialise the packaged_task using the return value from a std::bind. I have tried to decipher the implementations of all the related libstdc++ functions such as std::async, std::future, std::thread etc but with no luck.
The following code is the implementation of both the not working version and the working one. To get it to work uncomment the two /* --- WORKS*/ and comment the other related line.
#include <vector>
#include <deque>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <iostream>
#include <chrono>
#include <functional>
#include <windows.h>
class task
{
private:
struct task_implementation_base
{
virtual void execute() = 0;
};
template <class callable>
struct task_implementation : public task_implementation_base
{
task_implementation(callable&& f) : /*m_task(std::forward<callable>(f)) WORKS*/m_task(f) { }
void execute() { m_task(); }
//callable m_task; // WORKS
std::packaged_task<typename result_of<callable>::type> m_task;
};
template <class callable>
std::shared_ptr<task_implementation<callable>> make_routine(callable&& f)
{
return std::make_shared<task_implementation<callable>>(std::forward<callable>(f));
}
public:
template <class callable, class... arguments>
task(callable&& f, arguments&&... args) : m_function(make_routine(std::bind(std::forward<callable>(f), std::forward<arguments>(args)...))) {}
void operator()() { run(); }
void run() { m_function->execute(); }
private:
std::shared_ptr<task_implementation_base> m_function;
};
int testint(int i)
{
std::cout << "test6" << " :: ran from thread " << std::this_thread::get_id() << "\n";
fflush(stdout);
return i+100;
}
void test(const char* text)
{
std::cout << text << " :: ran from thread " << std::this_thread::get_id() << "\n";
fflush(stdout);
}
class testclass
{
public:
void print1() { test("test3"); }
void print2() { test("test4"); }
void print3(const char* text) { test(text); }
};
int main()
{
testclass testclass1;
testclass* testclass2 = new testclass;
task test1(test, "test1");
task test2([]() { test("test2"); });
task test3(&testclass::print1, &testclass1);
task test4(&testclass::print2, &*testclass2);
task test5(&testclass::print3, &*testclass2, "test5");
task test6(&testint, 1);
test1();
test2();
test3();
test4();
test5();
test6();
Sleep(2000);
return 0;
}
I'm thinking the problem is typename result_of<callable>::type. I'm guessing it doesn't properly evaluates to the return type of the callable function.
I'm using c++ (Built by MinGW-builds project) 4.8.0 20121225 (experimental) on a Windows 8 64bit. I'm suspecting the errors are irrelevant since I guess I'm just simply trying to get this work the wrong way but here is a pastebin for the errors anyway: errors
std::packaged_task not only takes the result type of the invoked function as a template argument but also the types of the arguments you are passing to the to be invoked function.
You can define them as follows:
// somewhere
int foo(bool, int);
// somewhere else
std::packaged_task<int(bool, int)> p(foo);
To fix your code you need to add two empty parenthesis pairs. What I explained above also applies to std::result_of.
std::packaged_task<typename std::result_of<callable()>::type()> m_task;
It is only response to main topic question. "How to implement"
Example short implementation:
template <typename Signature> /// <---- 1
class Task;
template <typename Res, typename... ArgTypes>
class Task<Res(ArgTypes...)> /// <---- 2
{
public:
template <typename Function>
explicit Task(Function&& callback)
: _task{std::forward<Function>(callback)}
{ }
void execute(ArgTypes... args) noexcept(false)
{
//...
_task(std::forward<ArgTypes>(args)...);
}
private:
std::packaged_task<Res(ArgTypes...)> _task;
};
Not sure why step 1 & 2 are required but I did the same as in lib implementation. Maybe someone could extend this response.