Task: XML_DOM serialization


C++: #include <cassert>
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <utility>
#include <vector>

#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlerror.h>
#include <libxml/xmlsave.h>
#include <libxml/xmlstring.h>
#include <libxml/xmlversion.h>

#ifndef LIBXML_TREE_ENABLED
#   error libxml was not configured with DOM tree support
#endif

#ifndef LIBXML_OUTPUT_ENABLED
#   error libxml was not configured with serialization support
#endif







template <typename F>
class [[nodiscard]] scope_exit
{
public:
    
     constexpr explicit scope_exit(F&& f) :
        f_{std::move(f)}
    {}

    ~scope_exit()
    {
        f_();
    }

    
    scope_exit(scope_exit const&) = delete;
    scope_exit(scope_exit&&) = delete;
    auto operator=(scope_exit const&) -> scope_exit& = delete;
    auto operator=(scope_exit&&) -> scope_exit& = delete;

private:
    F f_;
};


class libxml_error : public std::runtime_error
{
public:
    libxml_error() : libxml_error(std::string{}) {}

    explicit libxml_error(std::string message) :
        std::runtime_error{make_message_(std::move(message))}
    {}

private:
    static auto make_message_(std::string message) -> std::string
    {
        if (auto const last_error = ::xmlGetLastError(); last_error)
        {
            if (not message.empty())
                message += ": ";
            message += last_error->message;
        }

        return message;
    }
};

auto add_text(::xmlNode* node, ::xmlChar const* content)
{
    
    auto const text_node = ::xmlNewText(content);
    if (not text_node)
        throw libxml_error{"failed to create text node"};

    
    
    
    if (auto const res = ::xmlAddChild(node, text_node); not res)
    {
        ::xmlFreeNode(text_node);
        throw libxml_error{"failed to add text node"};
    }
    
    return text_node;
}

auto main() -> int
{
    
    constexpr auto no_xml_declaration = false;

    try
    {
        
        ::xmlInitParser();
        LIBXML_TEST_VERSION
        auto const libxml_cleanup = scope_exit{[] { ::xmlCleanupParser(); }};

        
        auto doc = ::xmlNewDoc(reinterpret_cast<::xmlChar const*>(u8"1.0"));
        if (not doc)
            throw libxml_error{"failed to create document"};
        auto const doc_cleanup = scope_exit{[doc] { ::xmlFreeDoc(doc); }};

        
        auto root = ::xmlNewNode(nullptr,
            reinterpret_cast<::xmlChar const*>(u8"root"));
        if (not root)
            throw libxml_error{"failed to create root element"};
        ::xmlDocSetRootElement(doc, root); 

        
        
        
        add_text(root, reinterpret_cast<::xmlChar const*>(u8"\n    "));

        
        if (auto const res = ::xmlNewTextChild(root, nullptr,
                reinterpret_cast<::xmlChar const*>(u8"element"),
                reinterpret_cast<::xmlChar const*>(
                    u8"\n        Some text here\n    "));
                not res)
            throw libxml_error{"failed to create child text element"};

        
        add_text(root, reinterpret_cast<::xmlChar const*>(u8"\n"));

        
        
        
        if constexpr (no_xml_declaration)
        {
            auto const save_context = ::xmlSaveToFilename("-", nullptr,
                XML_SAVE_NO_DECL);
            auto const save_context_cleanup = scope_exit{[save_context] {
                ::xmlSaveClose(save_context); }};

            if (auto const res = ::xmlSaveDoc(save_context, doc); res == -1)
                throw libxml_error{"failed to write tree to stdout"};
        }
        else
        {
            if (auto const res = ::xmlSaveFile("-", doc); res == -1)
                throw libxml_error{"failed to write tree to stdout"};
        }
    }
    catch (std::exception const& x)
    {
        std::cerr << "ERROR: " << x.what() << '\n';
        return EXIT_FAILURE;
    }
}