"Nested" make_unique calls failing on std::uninitialized_copy() - c++

New to C++ and mostly enjoying the learning curve, but struggling to solve this one.
I'm needing to satisfy this requirement in two different related places
std::unique_ptr [is typically used] as the element type in move-aware containers, such as std::vector, which hold pointers to dynamically-allocated objects (e.g. if polymorphic behavior is desired)
Compilable code (toggle the "working" and "not working" to cause build/fail):
#include<vector>
#include<memory>
class Entity;
class Action
{
public:
Action() = default;
};
class World {
std::vector<std::unique_ptr<Entity> > entities;
public:
World();
};
class Entity {
public:
Entity() = default;
};
class Animal: public Entity {
protected:
// Not working - log below
std::vector<std::unique_ptr<Action> > actions;
// Working fine but doesn't support polymorphic behaviour
//std::vector<Action> actions;
public:
Animal() = default;
};
class Person: public Animal
{
protected:
World& world;
public:
Person(World& world);
};
Person::Person(World& world) : world(world)
{
}
World::World()
{
Person first = Person(*this);
entities.push_back(std::make_unique<Person>(first));
}
Which barfs the error:
/usr/include/c++/9/bits/stl_uninitialized.h|307 col 37 error| required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<Action>*, std::vector<std::unique_ptr<Action> > >; _ForwardIterator = std::unique_ptr<Action>*; _Tp = std::unique_ptr<Action>]’
/usr/include/c++/9/bits/stl_vector.h|555 col 31 error| required from ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = std::unique_ptr<Action>; _Alloc = std::allocator<std::unique_ptr<Action> >]’
test.cpp|23 col 7 error| required from ‘typename std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = Person; _Args = {Person&}; typename std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<Person, std::default_delete<Person> >]’
test.cpp|48 col 54 error| required from here
/usr/include/c++/9/bits/stl_uninitialized.h|127 col 72 error| static assertion failed: result type must be constructible from value type of input range
So it seems that Person first is correctly created, but the error is coming from when make_unique<Person>(first) is handling the unique_ptr of first.actions. Maybe. I'm not following the process of why declaration is triggering the uninitialized_copy() merely through lack of understanding, and though there's reams of similar questions around unique_ptr, I find no direct references to the "nesting" thing I've got going on and 4 hours on this is about enough before resorting to asking here.
Not necessarily after a full solution, but some hints that will get me looking at the right direction would be much appreciated. Newb-level speak would be appreciated :-)
Thanks in anticipation.
Edit: Code snippet changed to be full and compilable using gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
My bad - vi coughed up the same make_unique() error as before, but compiling throws other errors - will not fix as question has been fully answered

std::vector<std::unique_ptr<Action>> is not copyable because std::unique_ptr<Action> is not copyable.
std::make_unique<Person>(first) tries to copy first.
This can be fixed by moving (std::make_unique<Person>(std::move(first))), but you can also forgo the intermediate local entirely:
entities.push_back(std::make_unique<Person>(*this));

Related

Storing objects of different derived classes of an abstract base class in an array in C++

I am a complete beginner trying to learn C++. So, this question might sound very cliche` . Please help me understand where I am going wrong.
The problem is described below.
Objective :
Define an abstract class Shape to act as interface. (Use pure virtual functions)
Override the methods in the derived classes Rectangle, Circle, Triangle.
Two functions are needed - a. read() -> To read input parameters(measurements) of a particular shape.
b. area() -> To calculate the total area of a shape.
Store objects(or pointers to objects) of different derived classes in an array.
Calculate area of each array member.
Calculate total area.
Code Snippets :
shape.hpp - Base class header file.
namespace generalShape
{
class Shape {
public :
virtual void read() = 0;
virtual double area() = 0 ;
};
}
rect.hpp - Derived class header file.
namespace rect2D
{
class Rectangle {
private :
double length;
double breadth;
public :
void read();
double area() ;
};
}
Similar header files are also written for two other derived classes,
namely circle.hpp and triangle.hpp
rect.cpp - Derived class implementation file.
using namespace rect2D;
// Read data (use it in main.cpp)
void Rectangle::read(/* no args */)
{
std::cin >> length;
std::cin >> breadth;
}
// To calculate area of a rectangle object
double Rectangle::area(/* no args */)
{
return length * breadth;
}
Similar implementation files are also written for two other derived classes,
namely circle.cpp and triangle.cpp
main.cpp - Client file.
using namespace generalShape;
using namespace rect2D;
// similarly use other namespaces as required
int main()
{
int number_of_shapes; // take input from user about number of objects
double total_area = 0;
/* Method 1 */
Shape **arr;
arr = new Shape*[size];
/* Method 2 */
//vector<Shape *> arr;
for (int i = 0; i < number_of_shapes; i++)
{
// some code to ask user about type of object
if (choice == 1)//based on user input
{
arr[i] = new Rectangle(); // for method 1
//arr.push_back(new Rectangle); // for method 2
}
// similar code for other derived class object types
// Code to calculate total_area of all objects in array.
arr[i]->read();
total_area += arr[i]->area();
}
return 0;
}
What is my Problem?
In main.cpp, I have specified Method 1 and Method 2 as comment lines in the main function.
Method 1 tries to use an array of base class pointers to point to derived class objects of different classes ( that's allowed, right ?). But it is giving an error.
error: cannot convert ‘rect2D::Rectangle*’ to ‘generalShape::Shape*’ in assignmentand similar errors for other derived class objects.
So, I tried to typecast my way around the problem.
// Shape is the base class with virtual methods read() and area().
// Rectangle,Triangle,Circle are derived classes which override these methods.
// main.cpp uses these header files to read user input using read() for each
// derived class and then calculate area using area(). The ultimate goal is to
// calculate total area of all derived class objects stored in an array.
arr[i] = (Shape*)(new Rectangle()) ;
Doing this, allows compilation without error. But when I try to execute, it gives segmentation fault. I am not sure about why this is happening. But I think it is because I defined pure virtual functions in the base class header file. Even if that is the case, I am not sure as to how to correct it.
Secondly, in Method 2, I tried to use vectors to achieve similar functionality after looking through other suggestions on stackoverflow. But now I am getting an error.
error: no matching function for call to ‘std::vector<generalShape::Shape*>::push_back(rect2D::Rectangle*)’
arr.push_back(new Rectangle);
^
In file included from /usr/include/c++/7/vector:64:0,
from src/main.cpp:2:
/usr/include/c++/7/bits/stl_vector.h:939:7: note: candidate: void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = generalShape::Shape*;
_Alloc = std::allocator<generalShape::Shape*>; std::vector<_Tp, _Alloc>::value_type = generalShape::Shape*]
push_back(const value_type& __x)
^~~~~~~~~
/usr/include/c++/7/bits/stl_vector.h:939:7: note: no known conversion for argument 1 from ‘rect2D::Rectangle*’ to ‘generalShape::Shape* const&’
/usr/include/c++/7/bits/stl_vector.h:953:7: note: candidate: void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = generalShape::Shape*; _Alloc = std::allocator<generalShape::Shape*>; std::vector<_Tp, _Alloc>::value_type = generalShape::Shape*]
push_back(value_type&& __x)
^~~~~~~~~
And I cannot understand how to correct this. So, I am stuck on this problem and am not able to solve it.Please help me to understand and correct the errors.
you just forgot to specify, that your class rect2D::Rectangle implements your abstract interface class generalShape::Shape.
You can do this by adding
: public generalShape::Shape
to the declaration of rect2D::Rectangle.
The fixed declaration is then:
namespace rect2D
{
class Rectangle : public generalShape::Shape {
private :
double length;
double breadth;
public :
void read();
double area() ;
};
}
One more hint: Please define a virtual destructor in your base class generalShape::Shape, if you intend to destroy your objects via the container holding the base class pointers.

Gtkmm: create a Gtk::ComboBox which lists Gtk::DrawingArea

I am trying to create a Gtk::ComboBox listing Gtk::DrawingArea widgets. I have followed this tutorial. So far, here is a minimal working example (i.e. which can be used to reproduce the issue) with Gtkmm3:
#include <gtkmm.h>
class NewPlayerRow : public Gtk::ListBoxRow
{
public:
NewPlayerRow();
private:
// Model for the combobox row: a disc with the appropriate player color...
struct NewPlayerDiscColorComboRowModel : public Gtk::TreeModel::ColumnRecord
{
NewPlayerDiscColorComboRowModel()
{
add(m_discColorIcon);
}
Gtk::TreeModelColumn<Gtk::DrawingArea> m_discColorIcon;
};
NewPlayerDiscColorComboRowModel m_comboRowModel;
Glib::RefPtr<Gtk::ListStore> m_listStore;
Gtk::ComboBox m_comboBox;
};
NewPlayerRow::NewPlayerRow()
{
// First, create and register the TreeModel:
m_listStore = Gtk::ListStore::create(m_comboRowModel);
m_comboBox.set_model(m_listStore);
// Then, populate the TreeModel:
Gtk::TreeModel::Row row = *(m_listStore->append());
row[m_comboRowModel.m_discColorIcon] = Gtk::DrawingArea();
row = *(m_listStore->append());
row[m_comboRowModel.m_discColorIcon] = Gtk::DrawingArea();
// Add the model columns to the Combo:
m_comboBox.pack_start(m_comboRowModel.m_discColorIcon);
add(m_comboBox);
}
int main(int argc, char** argv)
{
Glib::RefPtr<Gtk::Application> app{Gtk::Application::create(argc, argv, "com.github.bobmorane22.connectx")};
NewPlayerRow np;
Gtk::Window w;
w.add(np);
w.show_all();
return app->run(w);
}
When I compile this, I get the following error:
In file included from /usr/include/glibmm-2.4/glibmm/value.h:196:0,
from /usr/include/glibmm-2.4/glibmm/propertyproxy_base.h:25,
from /usr/include/glibmm-2.4/glibmm/propertyproxy.h:25,
from /usr/include/glibmm-2.4/glibmm/objectbase.h:24,
from /usr/include/glibmm-2.4/glibmm/object.h:29,
from /usr/include/pangomm-1.4/pangomm/context.h:32,
from /usr/include/gtkmm-3.0/gtkmm/widget.h:32,
from /usr/include/gtkmm-3.0/gtkmm/actiongroup.h:29,
from /usr/include/gtkmm-3.0/gtkmm/application.h:32,
from src/main.cpp:32:
/usr/include/glibmm-2.4/glibmm/value_custom.h: In instantiation of ‘static void Glib::Value<T>::value_copy_func(const GValue*, GValue*) [with T = Gtk::DrawingArea; GValue = _GValue]’:
/usr/include/glibmm-2.4/glibmm/value_custom.h:257:9: required from ‘static GType Glib::Value<T>::value_type() [with T = Gtk::DrawingArea; GType = long unsigned int]’
/usr/include/gtkmm-3.0/gtkmm/treemodelcolumn.h:134:64: required from ‘Gtk::TreeModelColumn<T>::TreeModelColumn() [with T = Gtk::DrawingArea]’
src/main.cpp:50:9: required from here
/usr/include/glibmm-2.4/glibmm/value_custom.h:283:33: error: use of deleted function ‘Gtk::DrawingArea::DrawingArea(const Gtk::DrawingArea&)’
dest_value->data[0].v_pointer = new(std::nothrow) T(source);
^
In file included from /home/morane/Programming/cpp/ConnectX/cxgui/include/GeometricShape.h:35:0,
from /home/morane/Programming/cpp/ConnectX/cxgui/include/Disc.h:35,
from src/../include/../include/CXDisc.h:35,
from src/../include/../include/GBDisc.h:37,
from src/../include/GameBoard.h:41,
from src/../include/GameWindow.h:17,
from src/main.cpp:34:
/usr/include/gtkmm-3.0/gtkmm/drawingarea.h:64:3: note: declared here
DrawingArea(const DrawingArea&) = delete;
which seems to indicate that the type in the combobox row model must be copyable for it to work. I have tried replacing the type Gtk::DrawingArea with std::string (which is copyable) in the above code and it builds fine and runs fine as well. I can see the combo box with its text rows.
Is there a way around this? I would really like to create a combo box which lists drawing areas.
EDIT Diving a little bit deeper in the error, I found that the issue is coming from the file value_custom.h in Glibmm. The following two functions seem to case the issue, since they try to access the copy member operation for the templated type (in my case Gtk::DrawingArea, which is not copyable, as mentionned above).
// static
template <class T>
GType Value<T>::value_type()
{
if(!custom_type_)
{
custom_type_ = Glib::custom_boxed_type_register(
typeid(CppType).name(),
&Value<T>::value_init_func,
&Value<T>::value_free_func,
&Value<T>::value_copy_func);
}
return custom_type_;
}
// static
template <class T>
void Value<T>::value_copy_func(const GValue* src_value, GValue* dest_value)
{
// Assume the source is not NULL. See value_init_func().
const T& source = *static_cast<T*>(src_value->data[0].v_pointer);
dest_value->data[0].v_pointer = new(std::nothrow) T(source);
}
I'm starting to feel like there is no way around this issue... The documentation for Glib::Value<T> even mentions that the type T has to implement copy assignment/construction.
If you have an idea, I'm all ears.
After more research, I have come to the conclusion that Gtk::ComboBoxes are simply not designed to hold widgets (hence Gtk::DrawingAreas) because is uses a Gtk::TreeModelColumn<T> in its TreeModel, where T needs to be copyable.
In other words, the types that compose your combobox model (ie. the types of what it actually lists when you click on it) must be copyable otherwise the framework won't let your code compile.
At first, the fact that widgets could not be copied made no sense to me, but after some research, I found this article, which clearly explains some of the (tough) issues that one might face when using copyable widgets.
In conclusion, it seems I am trying to accomplish something that would, in retrospective, be a bit weird UI-wise anyway and I will try to find some cleaner way to express my intent.

Two parameter template acting differently from one parameter in call

For an assignment (this is a fairly small part I've been stuck on, I'm not asking for anyone to do the homework), we were supplied with the following code and its call in the main file:
template <class number_type>
class Point {
public:
Point(const number_type& x_, const number_type& y_) : x(x_),y(y_) {}
// REPRESENTATION
number_type x;
number_type y;
};
template <class number_type, class label_type>
class QuadTree {
public:
QuadTree(const number_type& number_, const label_type& label_) : number(number_), label(label_){}
number_type number;
label_type label;
};
int main() {
std::vector< std::pair<Point<int>,char> > simple_points;
QuadTree<int,char> simple;
}
However, using a very similar style to the supplied coding produces a "no matching function" error in my terminal:
I'm confused as to why it doesnt work in this case, as well as what I can do to fix it.
As a note, we are not allowed to use c++11, structs, and a few other tools. Also, we are not permitted to change anything in the "main" function, so QuadTree simple; MUST stay as-is
main.cpp: In function ‘void simple_test()’:
main.cpp:110:22: error: no matching function for call to ‘QuadTree<int, char>::QuadTree()’
QuadTree<int,char> simple;
^
main.cpp:110:22: note: candidates are:
In file included from main.cpp:14:0:
quad_tree.h:56:5: note: QuadTree<number_type, label_type>::QuadTree(const number_type&, const label_type&) [with number_type = int; label_type = char]
QuadTree(const number_type& number_, const label_type& label_) : number(number_), label(label_){}
^
quad_tree.h:56:5: note: candidate expects 2 arguments, 0 provided
quad_tree.h:54:7: note: QuadTree<int, char>::QuadTree(const QuadTree<int, char>&)
class QuadTree {
^
quad_tree.h:54:7: note: candidate expects 1 argument, 0 provided
This code:
QuadTree<int,char> simple;
is attempting to call the default constructor. However, since you provide a constructor, the default constructor is implicitly deleted. The constructor you provided is:
QuadTree(const number_type& number_, const label_type& label_)
So you'd have to do:
QuadTree<int, char> simple(42, 'C');
Your QuadTree constructor takes 2 parameters, but you probably (based on your error message) have a definition like
QuadTree<int,char> simple;
which declares an object with no parameters passed to the constructor. You need to do something like
QuadTree<int, char> simple(2, 'a'); // need to specify the parameters
Note that because you defined a constructor, the compiler does not generate a default one for you anymore.

C++ Register class member var's at compile time

I try to implement a Java-Like annotation system using boost MPL and fusion.
Why is it needed:
I need to annotate member var's to have some special runtime features.
I register try them at compile time to my base clase like this:
class foo
{
INIT()
$REGISTER("test1")
int test1 = 5;
$REGISTER("b")
char* b = "rndmText";
....
}
My target is a combination of a pointer and a text like $REGISTER(&a,"a") but that's the future target...
The Base class handle all the necessary stuff.
The register macro create a fusion vector:
#define INIT() \
typedef boost::fusion::vector0<> BOOST_PP_CAT(registered, BOOST_PP_SUB(__COUNTER__,2)); \
boost::fusion::vector0<> BOOST_PP_CAT(list,BOOST_PP_SUB(__COUNTER__,2));
#define EXPORT(arg) \
typedef boost::fusion::result_of::push_back< BOOST_PP_CAT(registered, BOOST_PP_SUB(__COUNTER__,4)), const char*>::type BOOST_PP_CAT(registered, __COUNTER__); \
BOOST_PP_CAT(registered, BOOST_PP_DEC(__COUNTER__)) BOOST_PP_CAT(list, BOOST_PP_SUB(__COUNTER__,1)) = boost::fusion::make_list(BOOST_PP_CAT(list,BOOST_PP_SUB(__COUNTER__,7)), arg);
This expands (in my case) to:
typedef boost::fusion::vector0<> registered18;
boost::fusion::vector0<> list19;;
typedef boost::fusion::result_of::push_back< registered18, const char*>::type registered23;
registered23 list24 = boost::fusion::make_list(list19, "test1");;
int test1 = 5;
typedef boost::fusion::result_of::push_back< registered23, const char*>::type registered28;
registered28 list29 = boost::fusion::make_list(list24, "b");;
char* b = "rndmText";;
And here is the Problem:
boost::fusion::make_list(..., "test1") create a compiler error and i don't know how to fix it. This is the error:
boost::fusion::joint_view<Sequence,const boost::fusion::single_view<const char *>>::joint_view(const boost::fusion::joint_view<Sequence,const boost::fusion::single_view<const char *>> &)' : cannot convert argument 1 from 'boost::fusion::list<T,const char (&)[6],boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>' to 'const boost::fusion::joint_view<Sequence,const boost::fusion::single_view<const char *>> &'
Can someone help me or have a better idea?
Dominik
This is not really help with the compilation error (sorry) but it was too long for a comment.
Can someone help / or have a better idea?
I think you are abusing macros. Consider this client code instead:
class foo: public registered<foo> {
int test1 = 5;
char* b = "rndmText";
public:
foo();
virtual ~foo() = default;
};
foo::foo() : registered<foo>{ "foo" } {
register(&i, "i"); // part of the interface of the base class
register(&b, "b");
}
Explanation:
The same functionality is now provided by a base class. The CRTP implementation means that if you have two (or more) classes inheriting from registered, they are not in the same class hierarchy (because the adition of metadata should not impose a class relationship between unrelated concrete classes).
The implementation of registered<T> could use boost::fusion internally (or something else if you need it) and could hide the three meters long declarations behind a handy alias (e.g. using data_sequence = boost::fusion::vector0<>).
The INIT() part will go naturally in the construction of registered<T> instance (and public interface).
This implementation avoids macros completely and allows you to expose metadata to client code in a more elegant way, maybe by simply importing from the API of registered<T>.

C++ - calling a method from a class template

I'm currently having a problem with a class template in C++. I'm currently making a hash table.
I'm using a functor as a class template to specify my hash function for each instance of a table.
IE: one table has integers for its keys, strings for its values. Another could have strings for its keys and integers for its values, etc...
class HashString
{
public:
unsigned long operator()(std::string& key, const unsigned int tableSize)
{
// .....
}
};
template<typename keyType, typename valueType, class HashFunctor>
class HashTable
{
public:
// ....
private:
HashFunctor myHash;
};
And now let's say I want to call the method called "myHash" to hash a key, I would at first call it by doing:
myHash(key, table.size())
But gcc doesn't find a function overload for HashFuntor(string, unsigned int) for example.
Could someone tell me how I could call myHash?
(Note: I would not like to change my structure of functors)
edit: This is the error message I get from my actual solution
instantiated from ‘void tp3::Table<TypeClef, TypeDonnee, FoncHachage>::insert(const TypeClef&, const TypeDonnee&) [with TypeClef = int, TypeDonnee = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, FoncHachage = tp3::HacheString]’
no match for call to ‘(tp3::HacheString) (tp3::Table<int, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, tp3::HacheString>::HashEntry&)’
Edit: Everywhere it says HacheString is in fact HashString (I've translated my code to paste it here).
operator() in HashString is private and is probably not const-correct. It should be a const member function taking const std::string& as its first parameter. The second parameter does not need to be const.
You seem to be calling it with HashEntry as the second parameter. What is HashEntry? It takes an unsigned int!
That might already solve some of your problems.
I assume your HacheString / HashString difference is just a typo.