I am quite new to C++ and am trying to create a structure which will allow me to create a list of functions that I can use the same function (.create()
) for every member of that list. This would essentially let me have a registry of all the widgets within a tab.
The end goal would be to also hold my Tab
objects in a similar list.
My code (below) gives these errors in VS:
Error (active) E0289 no instance of constructor "std::list<_Ty, _Alloc>::list [with _Ty=std::shared_ptr<Program::Widget>, _Alloc=std::allocator<std::shared_ptr<Program::Widget>>]" matches the argument list Tabs.cpp line 12
Error (active) E0289 no instance of constructor "Program::WidgetA<T>::WidgetA [with T=Program::Widget]" matches the argument list Tabs.cpp line 13
Error (active) E0289 no instance of constructor "Program::WidgetB<T>::WidgetB [with T=Program::Widget]" matches the argument list Tabs.cpp line 14
// Tabs.cpp
#include "Tabs.h"
#include <string>
namespace Program
{
Tab::Tab(std::string t_id) { id = t_id; }
TabA::TabA(std::string t_id) : Tab::Tab(t_id)
{
std::string id_name = "A" + id;
WidgetList widgets = {
WidgetA<Widget>(id_name),
WidgetB<Widget>(id_name)
};
}
void TabA::generate()
{
for (auto it = widgets.begin(); it != widgets.end(); ++it)
{
(*it).create();
}
}
}
// Tabs.h
#pragma once
#include <string>
namespace Program
{
#ifndef TABS_H
#define TABS_H
class Tab
{
public:
Tab(std::string t_id);
Tab() = default;
std::string id;
WidgetList widgets;
};
class TabA : public Tab
{
public:
TabA(std::string t_id);
TabA() = default;
void generate();
};
#endif !TABS_H
}
// Widgets.cpp
#include "Widgets.h"
#include <string>
namespace Program
{
void WidgetA<Widget>::create()
{
// Code to launch WidgetA
}
void WidgetB<Widget>::create()
{
// Code to launch WidgetB
}
}
// Widgets.h
#pragma once
#include <string>
#include <list>
namespace Program
{
#ifndef WIDGETS_H
#define WIDGETS_H
class Widget
{
public:
Widget(const std::string& w_id) : id(w_id) {}
virtual ~Widget() {}
virtual void create() = 0;
std::string id;
};
template< typename T >
class WidgetA : public Widget
{
public:
WidgetA(const std::string& w_id, const T& data) : Widget(w_id), m_data(data);
void create();
private:
T m_data;
};
template< typename T >
class WidgetB : public Widget
{
public:
WidgetB(const std::string& w_id, const T& data) : Widget(w_id), m_data(data);
void create();
private:
T m_data;
};
typedef std::list< std::shared_ptr<Widget> > WidgetList;
#endif !WIDGETS_H
}
My current code comes from this question, though the few examples that it provides makes me unsure exactly how it was meant to be used/intended.
Edit:
I adjusted my code based on the feedback I got, and it seems to run now without errors. However, it seems that (*it).get()->create()
is not running WidgetA::create()
but instead the base class Widget::create()
, therefore not launching the Widget
s for that Tab
.
I can now also create a TabList
to dynamically create Tab
objects. I don't run into the problem here, because the generate()
method for all objects is not different for derived classes.
I saw a solution to this which included std::dynamic_pointer_cast
or std::static_pointer_cast
, but I'm not sure how to implement these.
Updated code:
// Tabs.cpp
#include "Tabs.h"
#include <string>
namespace Program
{
Tab::Tab(std::string t_id) { id = t_id; }
TabA::TabA(std::string t_id) : Tab::Tab(t_id)
{
std::string id_name = "A" + id;
WidgetList widgets = {
std::make_shared<Widget>(WidgetA(id_name)),
std::make_shared<Widget>(WidgetB(id_name))
};
}
void TabA::generate()
{
for (auto it = widgets.begin(); it != widgets.end(); ++it)
{
(*it).get()->create();
}
}
}
// Tabs.h
#pragma once
#include <string>
namespace Program
{
#ifndef TABS_H
#define TABS_H
class Tab
{
public:
Tab(std::string t_id);
Tab() = default;
virtual ~Tab() = default;
void generate();
std::string id;
WidgetList widgets;
};
typedef std::vector< std::shared_ptr<Tab> > TabList;
class TabA : public Tab
{
public:
TabA(std::string t_id);
TabA() = default;
};
#endif !TABS_H
}
// Widgets.cpp
#include "Widgets.h"
#include <string>
namespace Program
{
void WidgetA<Widget>::create()
{
// Code to launch WidgetA
}
void WidgetB<Widget>::create()
{
// Code to launch WidgetB
}
}
// Widgets.h
#pragma once
#include <string>
#include <list>
#include <iostream>
namespace Program
{
#ifndef WIDGETS_H
#define WIDGETS_H
class Widget
{
public:
Widget(const std::string& w_id) : id(w_id) {}
Widget() = default;
virtual ~Widget() = default;
void create()
{
std::cout << "Base Widget create()";
};
std::string id;
};
class WidgetA : public Widget
{
using Widget::Widget;
public:
void create();
};
class WidgetB : public Widget
{
using Widget::Widget;
public:
void create();
};
typedef std::list< std::shared_ptr<Widget> > WidgetList;
#endif !WIDGETS_H
}
Edit 2 I have gotten it to run now, by changing the following:
class Widget
{
public:
Widget(const std::string& w_id) : id(w_id) {}
Widget() = default;
virtual ~Widget() = default;
virtual void create() { std::cout << "Base Widget create()\n"; }
std::string id;
};
typedef std::vector< std::shared_ptr<Widget> > WidgetList;
class PropertiesWidget : public Widget
{
using Widget::Widget;
public:
void create() override;
};
Making the default
constructors, and adding override
to the create()
method
void Tab::generate()
{
for (auto it = widgets.begin(); it != widgets.end(); ++it)
{
(*it)->create();
}
}
Generate is now a base method and I removed .get()
TabA::TabA(std::string t_id) : Tab::Tab(t_id)
{
std::string id_name = "A" + id;
widgets = {
std::make_shared<WidgetA>(id_name),
std::make_shared<WidgetB>(id_name)
};
}
Altered the std::makeshared
This resource was most helpful in solving my problem.
I'm not entirely sure if this is the best implementation for this scenario, but it seems to be ok for now. Any feedback, improvements, or redundancies would be greatly appreciated.