0

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 Widgets 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.

LeaG
  • 13
  • 2
  • 1
    Your list is expecting shared pointers but you're passing in `WidgetA` objects – Alan Birtles Apr 11 '23 at 20:27
  • 1
    You need to dynamically allocate your `WidgetA` and `WidgetB`. See [`std::make_shared`](https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared). Also `WidgetA` and `WidgetB` both require two constructor parameters, but you're only giving them one. – Miles Budnek Apr 11 '23 at 20:27
  • 1
    Polymorphism, rather than templates, is the way to go here. – Paul Sanders Apr 11 '23 at 20:36
  • 4
    Side note. `std::list` is generally implemented as a linked list and brings two things to the game: rapid insert and delete of entries you already have found and hold an iterator for and very forgiving [iterator invalidation rules](https://stackoverflow.com/q/6438086/4581301). If you've not taking advantage of those two things, know that `list` pretty much sucks at everything else, and you probably want the more array-like `std::vector` – user4581301 Apr 11 '23 at 20:39
  • Given that the template parameter `T` is `Widget`, which is an abstract class with a pure virtual function, what exactly do you expect `T m_data;` to accomplish, and how? – Sam Varshavchik Apr 11 '23 at 21:21
  • @SamVarshavchik `m_data` was a parameter I forgot to remove from [the question I got my code from](https://stackoverflow.com/questions/3559412/how-to-store-different-data-types-in-one-list-c). – LeaG Apr 12 '23 at 10:46
  • @PaulSanders are there any resources that show how to efficiently and effectively use polymorphism in my case? – LeaG Apr 12 '23 at 10:47
  • 2
    It's always helpful if the question shows real, actual code that demonstrates the issue, instead of something that's not the real, actual code. The only thing that results from fake code is fake answers. – Sam Varshavchik Apr 12 '23 at 10:47
  • Sounds like you want a container of pointers to objects with `virtual` functions. – Jesper Juhl Apr 12 '23 at 11:12
  • Why on earth are WidgetA and WidgetB templates? – n. m. could be an AI Apr 12 '23 at 11:59
  • This can't be exactly your code, because `WidgetList` doesn't name a type in Tabs.h – Caleth Apr 12 '23 at 12:45
  • `std::make_shared(WidgetA(id_name))` should be `std::make_shared(id_name)`, similarly for `WidgetB`. A `std::shared_ptr` implicitly converts to a `std::shared_ptr`, which is what `WidgetList` holds – Caleth Apr 12 '23 at 12:47

0 Answers0