Adapting strongly typed enum to ints without abstraction overhead
There is a code like below: #include #include #include #include template struct IPushChannel { virtual void push(T) = 0; }; template struct PushChannel : IPushChannel { void push(T) override { std::cout

There is a code like below:
#include
#include
#include
#include
template
struct IPushChannel {
virtual void push(T) = 0;
};
template
struct PushChannel : IPushChannel {
void push(T) override
{
std::cout << "push\n";
// do sth
}
};
template
std::shared_ptr> getChannel(std::string channel_id)
{
// For the purpose of question let's allocate a channel
// normally it performs some lookup based on id
static auto channel = std::make_shared>();
return channel;
}
enum class SomeEnum { E1, E2, E3 };
Below is V1 code
namespace v1 {
template
struct PushProxy {
PushProxy(std::shared_ptr> t) : ptr_{t} {}
void push(T val)
{
if (auto ptr = ptr_.lock())
{
ptr->push(val);
}
else {
std::cout << "Channel died\n";
}
}
std::weak_ptr> ptr_;
};
template
struct EnumAdapter : IPushChannel {
EnumAdapter(std::shared_ptr> ptr) : ptr_{ptr} {}
void push(T)
{
ptr_.lock()->push(static_cast(123));
}
std::weak_ptr> ptr_;
};
template
PushProxy getProxy(std::string channel_id) {
if constexpr (std::is_enum_v) {
auto channel = getChannel(channel_id);
auto adapter = std::make_shared>(channel);
return PushProxy{adapter};
}
else {
return PushProxy{getChannel(channel_id)};
}
}
}
Below is V2 code
namespace v2 {
template
struct PushProxy {
template
PushProxy(Callable func) : ptr_{func} {}
void push(T val)
{
if (auto ptr = ptr_())
{
ptr->push(val);
}
else {
std::cout << "Channel died\n";
}
}
std::function>()> ptr_;
};
template
struct WeakPtrAdapter
{
std::shared_ptr operator()()
{
return ptr_.lock();
}
std::weak_ptr> ptr_;
};
template
struct EnumAdapter {
struct Impl : public IPushChannel {
void useChannel(std::shared_ptr> channel)
{
// Keep the channel alive for the upcoming operation.
channel_ = channel;
}
void push(T value)
{
channel_->push(static_cast(value));
// No longer needed, reset.
channel_.reset();
}
std::shared_ptr> channel_;
};
std::shared_ptr> operator()()
{
if (auto ptr = ptr_.lock())
{
if (!impl_) {
impl_ = std::make_shared();
}
// Save ptr so that it will be available during the opration
impl_->useChannel(ptr);
return impl_;
}
impl_.reset();
return nullptr;
}
std::weak_ptr> ptr_;
std::shared_ptr impl_;
};
template
PushProxy getProxy(std::string channel_id) {
if constexpr (std::is_enum_v) {
return PushProxy{EnumAdapter{getChannel(channel_id)}};
}
else {
return PushProxy{WeakPtrAdapter{getChannel(channel_id)}};
}
}
}
Main
void foo_v1()
{
auto proxy = v1::getProxy("channel-id");
proxy.push(SomeEnum::E1);
}
void foo_v2()
{
auto proxy = v2::getProxy("channel-id");
proxy.push(SomeEnum::E1);
}
int main()
{
foo_v1();
foo_v2();
}
Let's say that channels are entities that allow to push some data through it. Supported types are e.g. int, std::string but also enums. Let's assume that channels are fetched from some external service and enums are abstracted as ints.
In my library I want to give the user to opportunity to operate on strongly typed enums not ints, the problem is that in the system these channels have been registered as ints.
When the user calls for channel's proxy, it provides the Proxy's type, so for e.g. int it will get PushProxy, for MyEnum, it will get PushProxy
However, as you can see when the user wants to get enum proxy, the library looks for int channel, thus I cannot construct PushProxy with IPushChannel because the type does not match.
So I though that maybe I could introduce some adapter that will covert MyEnum to int, so that user will use strongly types enum PushProxy where the value will be converted under the hood.
The channels in the system can come and go so that's why in both cases I use weak_ptr.
V1 In V1 the problem is that I cannot simply allocated EnumAdapter and pass it to PushProxy because it gets weak_ptr, which means that the EnumAdapter will immediately get destroyed. So this solution does not work at all.
V2 In V2 the solution seems to be working fine, however the problem is that there can be hundreds of Proxies to the same channel in the system, and each time the Proxy gets constructed and used, there is a heap allocation for EnumAdapter::Impl. I'm not a fan of premature optimization but simply it does not look well.
What other solution would you suggest? I would like to avoid storing adapter somewhere externally. Also it would only solve the problem partially as the Adapter would be allocated on the heap each time one gets an access to proxy.