dimanche 11 octobre 2020

C++ virtual constructors and Virtualization of non-member functions (via delegation to a virtual member function) [duplicate]

I am still new to programming as a subject and I realised that I need to ask for help. Please be patient with me.

For this assignment I have to implement virtual construction using the factory method design pattern and Virtualization of non-member functions (via delegation to a virtual member function).

The construct() function will create a formatter object (Text, Markdown or Html) based on choice and will create headers, paragraphs and block quotes. The global function save() saves the text element into a stream. The files included are the only ones permitted to be used.

Thank you to those that answered previously.

The main.cpp file below contains the driver code from my professor:

    #include "MemoryAlloc.h"
    #include "Constructor.h"
    
    void process(IFormatterElement* const element)
    {
        if (element)
        {
            try
            {
                if (ISavable* const savable = dynamic_cast<ISavable* const>(element))
                {
                    save(*savable, std::cout);
                }
                delete element;
            }
            catch (...)
            {
                delete element;
                throw;
            }
        }
    }
    
    int main()
    {
        std::cerr << "Choose a formatter:\n";
        std::vector<std::string> options = get_options();
        for (size_t i = 0; i < options.size(); ++i)
        {
            std::cerr << "\t" << (i + 1) << ". " << options[i] << "\n";
        }
    
    std::cerr
    << "\tother to exit\n"
    << "Your choice: ";
    
    int selected_option;
    if (std::cin >> selected_option)
    {
        if (IFormatterFactory* factory = construct(--selected_option))
        {
            std::cerr << "\nYou chose \"" << options[selected_option] << "\"." << std::endl;
            try
            {
                process(factory->create_begin());
                process(factory->create_header1("Assignment result"));
                process(factory->create_paragraph("This file represents an assignment result."));
                process(factory->create_header2("Initial paragraph"));
                process(factory->create_paragraph("This is the first paragraph of the result file."));
                process(factory->create_header2("Final paragraph"));
                process(factory->create_paragraph("This is the second paragraph of the result file."));
                process(factory->create_blockquote("This is a quote."));
                process(factory->create_paragraph("This is the third paragraph of the result file."));
                process(factory->create_paragraph("This is the last paragraph of the result file."));
                process(factory->create_end());
            }
            catch (...)
            {
                std::cerr << "An exception has been thrown!" << std::endl;
            }
            delete factory;
        }
        else
        {
            std::cerr << "Exiting..." << std::endl;
        }
    }
}

Interface class IFormatterelement.h to support operations needed in process() as defined by me.

#ifndef _IFORMATTERELEMENT_H_
#define _IFORMATTERELEMENT_H_

class IFormatterElement
{
public:
    virtual ~IFormatterElement() = default;  
};

#endif // _IFORMATTERELEMENT_H_

Interface class IFormatterfactory.h to support operations needed in main() as defined by me.

#ifndef _IFORMATTERFACTORY_H_
#define _IFORMATTERFACTORY_H_

#include <string>

#include "IFormatterElement.h"

class IFormatterFactory : public IFormatterElement
{
public:
    virtual IFormatterFactory* create_begin() = 0;
    virtual IFormatterFactory* create_end() = 0;
    virtual IFormatterFactory* create_header1(const std::string& text) = 0;
    virtual IFormatterFactory* create_header2(const std::string& text) = 0;
    virtual IFormatterFactory* create_paragraph(const std::string& text) = 0;
    virtual IFormatterFactory* create_blockquote(const std::string& text) = 0;
};

#endif // _IFORMATTERFACTORY_H_

Interface class ISavable.h to be realized by classes that saves into a stream.

#ifndef _ISAVABLE_H_
#define _ISAVABLE_H_

#include <iostream>

struct ISavable
{
    virtual void save(std::ostream& stream) = 0;
    virtual ~ISavable() = default;
};

#endif // _ISAVABLE_H_

Global functions that are needed. IFormatterFactory* construct(int selected_option); is the factory method and void save(ISavable& savable, std::ostream& stream); will be a polymorphic call to save into a stream

#ifndef _CONSTRUCTOR_H_
#define _CONSTRUCTOR_H_

#include <iostream>
#include <vector>
#include <string>

#include "IFormatterFactory.h"
#include "ISavable.h"

// Produces a list of available formatter names.
std::vector<std::string> get_options();

// Constructs a formatter based on the index of its name from get_options().
IFormatterFactory* construct(int selected_option);

// Saves an object to a stream.
void save(ISavable& savable, std::ostream& stream);

#endif // _CONSTRUCTOR_H_

Below is my implementation of Constructor.cpp

#include "Constructor.h"
#include "Text.h"
#include "Markdown.h"
#include "Html.h"

std::vector<std::string> get_options()
{
    std::vector<std::string> formatters;
    formatters.push_back("Text");
    formatters.push_back("Markdown");
    formatters.push_back("Html");
    return formatters;
}

IFormatterFactory* construct(int selected_option)
{
    if(selected_option == 0)
        return new Text;
    
    if(selected_option == 1)
        return new Markdown;
    
    else
        return new Html;
}

void save(ISavable& savable, std::ostream& stream)
{
    savable.save(stream);
}
My implementation of Text class and its member functions.

#ifndef _TEXT_H_
#define _TEXT_H_

#include "ISavable.h"
#include "IFormatterFactory.h"

class Text : public IFormatterFactory, public ISavable
{
public:
    Text();
    Text* create_begin();
    Text* create_end();
    void save(std::ostream& stream);
    Text* create_header1(const std::string& text);
    Text* create_header2(const std::string& text);
    Text* create_paragraph(const std::string& text);
    Text* create_blockquote(const std::string& text);

private:
    std::string _input;
};

Text::Text() : _input{}
{
}

Text* Text::create_begin()
{
    return this;
}

Text* Text::create_end()
{
    return this;
}

Text* Text::create_header1(const std::string& text)
{
    _input = text + "\n";
    return this;
}

Text* Text::create_header2(const std::string& text) 
{
    _input = + "\n" + text + "\n";
    return this;
}

Text* Text::create_paragraph(const std::string& text)  
{
    _input = text + "\n";
    return this;
}

Text* Text::create_blockquote(const std::string& text)
{
    _input = "\"" + text + "\"" + "\n";
    return this;
}

void Text::save(std::ostream& stream)
{
    stream << _input;
}

#endif

My implementation of Markdown class and its member functions.

#ifndef _MARKDOWN_H_
#define _MARKDOWN_H_

#include "ISavable.h"
#include "IFormatterFactory.h"

class Markdown : public IFormatterFactory, public ISavable
{
public:
    Markdown();
    Markdown* create_begin();
    Markdown* create_end();
    void save(std::ostream& stream);
    Markdown* create_header1(const std::string& text);
    Markdown* create_header2(const std::string& text);
    Markdown* create_paragraph(const std::string& text);
    Markdown* create_blockquote(const std::string& text);
    
private:
    std::string _input;
    char _count{1}; 
};

Markdown::Markdown() : _input{}
{
}

Markdown* Markdown::create_begin()
{
    return this;
}

Markdown* Markdown::create_end()
{
    return this;
}

Markdown* Markdown::create_header1(const std::string& text)
{
    _input = std::string("\n") + std::string("# 1. ") + text + "\n";
    return this;
}

Markdown* Markdown::create_header2(const std::string& text)
{
    _input = std::string("\n") + std::string("## 1.") + (_count++) + ". " + text + "\n";
    return this;
}

Markdown* Markdown::create_paragraph(const std::string& text)
{
    _input = text + "\n";
    return this;
}

Markdown* Markdown::create_blockquote(const std::string& text)
{
    _input = + "> " + text + "\n\n";
    return this;
}

void Markdown::save(std::ostream& stream)
{
    stream << _input;
}

#endif
My implementation of Html class

#ifndef _HTML_H_
#define _HTML_H_

#include "ISavable.h"
#include "IFormatterFactory.h"

class Html : public IFormatterFactory, public ISavable
{
public:
    Html();
    Html* create_begin();
    Html* create_end();
    void save(std::ostream& stream);
    Html* create_header1(const std::string& text);
    Html* create_header2(const std::string& text);
    Html* create_paragraph(const std::string& text);
    Html* create_blockquote(const std::string& text);
    
private:
    std::string _input;
    char _count{1};
    char _end{1};
};

Html::Html() : _input{}
{
}

Html* Html::create_begin()
{
    _input = std::string("<!doctype html>") + "\n" + "<html lang=en>" + "\n" 
    + "<style>blockquote { border-left: 4px solid #d0d0d0; " 
    + "padding: 4px; }</style>"
    + "\n" + "<head>" + "\t<meta charset=utf-8>" 
    + "\n" + "\t<title>Result</title>"
    + "</head>\n" + "<body>\n";
    return this;
}

Html* Html::create_end()
{
    return this;
}

Html* Html::create_header1(const std::string& text) 
{
    _input = "\t<h1>1. " + text + "</h1>" + "\n";
    return this;
}

Html* Html::create_header2(const std::string& text) 
{
    _input = std::string("\t<h2>1.") + (_count++) + std::string(". ") + text + "</h2>" + "\n";
    return this;
}

Html* Html::create_paragraph(const std::string& text)  
{
    _input = "\t<p>" + text + "</p>" + "\n";
    if(_end==5)
    {
      _input + "</body>" + "\n";
      _input + "</html>" + "\n";
    }
    else
    {
        _end++;
    }       
    return this;
}

Html* Html::create_blockquote(const std::string& text) 
{
    _input = "\t<blockquote>" + text + "</blockquote>" + "\n";
    return this;
}

void Html::save(std::ostream& stream)
{
    stream << _input;
}

#endif

My idea was to use std::string to store the lines or characters and save it into std::ostream& stream` but when I compile the program, I will get SEGMENTATION FAULT for all 3 formatter classes.

I used gdb to find out the cause of the SEGMENTATION FAULT. I got this message:

Choose a formatter:
    1. Text
    2. Markdown
    3. Html
    other to exit
Your choice: 3

You chose "Html".
<!doctype html>
<html lang=en>
<style>blockquote { border-left: 4px solid #d0d0d0; padding: 4px; }</style>
<head>  <meta charset=utf-8>
        <title>Result</title></head>
<body>

Thread 1 "main" received signal SIGSEGV, Segmentation fault.
0x0000000180325278 in _gm_ () from /usr/bin/cygwin1.dll
(gdb) quit

I figured the cause of this error is the call to process(factory>create_begin()); in main() as given by my prof, which I cannot change.

I am truly stuck at this point of time. I could not reach my professor for help so I am reaching out to the online community for help. Is the member function create_begin() implemented wrongly in my Text, Markdown and Html class?

Aucun commentaire:

Enregistrer un commentaire