1

I've been going over my code and fiddling but I can't seem to figure out why I'm not getting the expected output but instead random symbols.

The expected output is: JoeUPSReminderPick up your package! 54.23

I get the < but anything after that is gibberish. Any help would be appreciated.

#include <cstddef>        // for std::size_t
#include <iostream>
#include <memory>
#include <ostream>
#include <string>
#include <utility>        // for std::move, std::forward
#include <vector>

class xml_node_base 
{
    public:
      virtual ~xml_node_base() = default;
      void output(std::ostream& os) const
      {
        do_output_open(os);
        do_output_body(os);
        do_output_close(os);
      }
    protected:
      virtual void do_output_open(std::ostream& os) const = 0; // abstract
      virtual void do_output_body(std::ostream&) const { } // not abstract
      virtual void do_output_close(std::ostream& os) const = 0; // abstract
};

using xml_node_base_t = std::shared_ptr<xml_node_base>;
using xml_node_bases_t = std::vector<xml_node_base_t>;

template <typename T, typename... Args>
inline xml_node_base_t make_xml_node(Args&&... args)
{
      return std::make_shared<T>(std::forward<Args>(args)...);
}   

class xml_node: virtual public xml_node_base
{
    private:
      std::string const& node_name;
    public:
      xml_node() = delete;
      xml_node(std::string const& name) : node_name(name)
      {
      };
    protected:
      void do_output_open(std::ostream& os) const override
      {
        os << "<" << node_name << ">";
      };
      void do_output_close(std::ostream& os) const override
      {
        os << "</" << node_name << ">";
      };
};

class xml_node_with_children: public xml_node

{
    private:
      xml_node_bases_t children_;
    public:
      xml_node_with_children() = delete;
      xml_node_with_children(std::string const& name) : xml_node(name)
      {
      };
      xml_node_with_children(std::string const& name, std::size_t reserve) : xml_node_with_children(name)
      {
        children_.reserve(reserve);
      };
      xml_node_with_children(std::string const& name, xml_node_bases_t children) : xml_node(name), children_(std::move(children))
      {
      };
    protected:
      auto& children() { return children_; };
      auto const& children() const { return children_; };
      void do_output_body(std::ostream& os) const
      {
        for (auto const& c : children_)
        {
            c -> output(os);
        }
      };
};

template <typename T>
class value_node : public xml_node
{
    private:
      T datum;
    protected:
      void do_output_body(std::ostream& os) const 
      {
        os << datum;
      }
    public:
      value_node(std::string const& name, T const& v) : xml_node(name), datum(v)
      {
      }
};

class note : public xml_node_with_children
{
    public:
      note() = delete;
      note(std::string const& to, std::string const& from, std::string const& subject, std::string const& message) : xml_node_with_children("note", 4)
      {
        children().push_back(make_xml_node<value_node<std::string>>("to",to));
        children().push_back(make_xml_node<value_node<std::string>>("from",from));
        children().push_back(make_xml_node<value_node<std::string>>("subject",subject));
        children().push_back(make_xml_node<value_node<std::string>>("message",message));
      }
};

class root : protected xml_node_with_children
{
    public:
      using xml_node_with_children::xml_node_with_children;
      using xml_node_with_children::output;
      using xml_node_with_children::children;
};

std::ostream& operator<<(std::ostream& os, root const& r)
{
    r.output(os);
    return os;
}

int main()
{
  root notes{"notes"};
  notes.children().push_back(
    make_xml_node<note>("Joe", "UPS", "Reminder", "Pick up your package!")
  );
  notes.children().push_back(
    make_xml_node<value_node<double>>("priority",54.23)
  );
  std::cout << notes << '\n';
}

I think the problem could be with the for loop on line 90, since I'm not too familiar with the -> operator.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
mariacat
  • 25
  • 2

1 Answers1

5
  std::string const& node_name;

      xml_node(std::string const& name) : node_name(name)

This class member is a reference, and the constructor initializes it from a reference that gets passed in as a parameter to the constructor.

Let's trace things all the way back and see where the parameter, to the constructor, originally comes from. Here's one example:

children().push_back(make_xml_node<value_node<std::string>>("to",to));

The parameter is a literal string, "to".

C++ is very famous, and is very well known for giving everyone every opportunity to shoot themself in the foot, if that's what they really want to do, so:

  1. A temporary std::string object gets constructed.

  2. A reference to this object gets passed as a parameter, through several onion layers of constructors, elephant-style.

  3. A reference to this object gets saved in a member of the base class.

  4. After all the constructors finish, and this statement finishes executing, the temporary std::string object, that owns this "to" gets destroyed.

  5. The instance of the class now has a reference to a destroyed object, in its node_name.

  6. This is repeated for all the other objects in the shown code that get constructed like that.

  7. You just shot yourself in the foot.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148