'Optimization-friendly Polymorphic NVI adapter class for Runtime Polymorphism (customizable ownership)
I have a polymorphic NVI class to provide an adapter for Runtime-Polymorphism.
Type-erasure is used for loose coupling.
The non-owning implementation can be optimized out in a compile-time context:
// usage just for reference
printer referenced{"Referenced"};
const auto &polymorphicPrinter = polymorphic(referenced);
polymorphicPrinter.print(8.15);
// how to optimize this?
const auto &owningPolymorhic = polymorphic(std::make_unique<printer>("Owned");
However, I am not sure what is a simple or elegant way to allow passing the ownership and still have optimization like in the above case when it is possible.
Here is my naive implementation. I also added std::bad_function_call for invalid state.
// api_header.h
#pragma once
#include <memory>
#include <string_view>
#include <functional>
class polymorphic final
{
public:
template <typename T>
polymorphic(const T& t)
: pVTable_(getVTable(t)),
pObj_(std::addressof(t), [](const void*){})
{}
template <typename T>
polymorphic(std::unique_ptr<const T> t)
: pVTable_(getVTable(*t)),
pObj_(t.release(), [](const void* pObj){
delete static_cast<const T*>(pObj);
})
{}
template <typename T>
polymorphic(std::unique_ptr<T> t)
: polymorphic(std::unique_ptr<const T>(t.release()))
{}
polymorphic(const polymorphic&) = delete;
polymorphic& operator=(const polymorphic&) = delete;
polymorphic(polymorphic&& other) noexcept
: pVTable_(other.pVTable_),
pObj_(std::move(other.pObj_))
{
other.pVTable_ = polymorphic::getInvalidVTable();
}
polymorphic& operator=(polymorphic &&other) noexcept
{
pVTable_ = other.pVTable_;
pObj_ = std::move(other.pObj_);
other.pVTable_ = polymorphic::getInvalidVTable();
return *this;
}
template <typename T>
void print(T v) const
{
pVTable_->print(pObj_.get(), v);
}
private:
polymorphic() = default;
struct v_table {
virtual void print(const void *, double) const
{
throw std::bad_function_call();
}
virtual void print(const void *, std::string_view) const
{
throw std::bad_function_call();
}
protected:
~v_table() = default;
};
static const v_table *getInvalidVTable()
{
struct : v_table {} constexpr static invalidVTable{};
return &invalidVTable;
}
template <typename T>
static const v_table *getVTable(const T&)
{
struct : v_table {
void print(const void *pObj, double v) const override
{
static_cast<const T*>(pObj)->print(v);
}
void print(const void *pObj, std::string_view v) const override
{
static_cast<const T*>(pObj)->print(v);
}
} constexpr static vTable{};
return &vTable;
}
private:
const v_table *pVTable_ = getInvalidVTable();
// TODO: optimisation-friendly?
std::unique_ptr<const void, std::function<void(const void*)>> pObj_;
};
// main.cpp
#include "api_header.h"
#include <cstddef>
#include <string>
#include <iostream>
class printer
{
public:
template <size_t L>
explicit printer(const char (&name)[L])
: name_(name, L)
{}
void print(int v) const
{
std::cout << name_ << ": " << v << std::endl;
}
void print(double v) const
{
std::cout << name_ << ": " << v << std::endl;
}
void print(std::string_view v) const
{
std::cout << name_ << ": " << v << std::endl;
}
~printer()
{
std::cout << name_ << " destroyed\n";
}
private:
std::string name_;
};
int main()
{
printer pReferenced{"referenced"};
{
const auto &polyReferenced = polymorphic(pReferenced);
polyReferenced.print(4);
polyReferenced.print(8.15);
polyReferenced.print("oceanic");
std::cout << "non-owning polymorphic destroyed\n\n";
}
{
const auto &polyOwned = polymorphic(std::make_unique<printer>("owned"));
polyOwned.print(4);
polyOwned.print(8.15);
polyOwned.print("oceanic");
}
std::cout << "owning polymorphic destroyed\n\n";
std::cout << "Invalidating polymorphic reference\n";
try {
auto invalidReferenced = polymorphic(pReferenced);
auto polyMoved = std::move(invalidReferenced);
polyMoved.print("I have been moved");
invalidReferenced.print("This will throw");
}
catch (const std::bad_function_call &e)
{
std::cout << "error: " << e.what() << std::endl;
}
}
This code: https://godbolt.org/z/Pc8981ns8
Is it possible to make this code optimization-friendly?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
