Replacing virtual
With std::variant<>
(In Real Life)¶
Classic OO Design¶
Source Hierarchy¶
#pragma once
#include <string>
class Source
{
public:
virtual ~Source() = default;
virtual const char* get() = 0;
};
#pragma once
#include "source.h"
class SourceCopyable : public Source
{
public:
SourceCopyable(const std::string& resource);
const std::string& resource() const { return _resource; }
const char* get() override;
private:
std::string _resource;
};
#pragma once
#include "source.h"
class SourceMovable : public Source
{
public:
SourceMovable(const char* resource);
SourceMovable(SourceMovable&&);
SourceMovable& operator=(SourceMovable&&);
~SourceMovable();
const char* resource() const { return _resource; }
const char* get() override;
private:
char* _resource;
};
Sink Hierarchy¶
#pragma once
#include <string>
class Sink
{
public:
virtual ~Sink() = default;
virtual void put(const std::string&) = 0;
};
#pragma once
#include "sink.h"
class SinkCopyable : public Sink
{
public:
SinkCopyable(const std::string& resource);
const std::string& resource() const { return _resource; }
void put(const std::string& s) override;
private:
std::string _resource;
};
#pragma once
#include "sink.h"
class SinkMovable : public Sink
{
public:
SinkMovable(const char* resource);
SinkMovable(SinkMovable&&);
SinkMovable& operator=(SinkMovable&&);
~SinkMovable();
const char* resource() const { return _resource; }
void put(const std::string& s) override;
private:
char* _resource;
};
Loop In The Middle¶
#pragma once
#include "source.h"
#include "sink.h"
class Loop
{
public:
Loop(Source* source, Sink* sink)
: _source(source), _sink(sink) {}
void doit(unsigned ntimes)
{
while (ntimes--) {
std::string s = _source->get(); // <-- dynamic dispatch
_sink->put(s); // <-- dynamic dispatch
}
}
private:
Source* _source; // <-- interface
Sink* _sink; // <-- interface
};
Omitting virtual
: Cram All Alternatives Into std::variant<>
¶
Source Non-Hierarchy¶
#pragma once
#include <string>
#include <variant>
template <class... Sources>
class Source
{
public:
Source() = default;
template <class ConcreteSource>
Source(ConcreteSource s)
: _manyfold(s) {}
std::string get()
{
return std::visit(Visitor(), _manyfold);
}
private:
struct Visitor
{
std::string operator()(auto source) { return source.get(); }
};
private:
std::variant<Sources...> _manyfold;
};
#pragma once
#include <string>
class SourceA
{
public:
std::string get()
{
return "Source A";
}
};
#pragma once
#include <string>
class SourceB
{
public:
std::string get()
{
return "Source B";
}
};
Sink Non-Hierarchy¶
#pragma once
#include <string>
#include <variant>
template <class... Sinks>
class Sink
{
public:
Sink() = default;
template <class ConcreteSink>
Sink(ConcreteSink s)
: _manyfold(s) {}
void put(const std::string& str)
{
return std::visit(Visitor(str), _manyfold);
}
private:
struct Visitor
{
Visitor(const std::string& str) : str(str) {}
void operator()(auto sink) { return sink.put(str); }
const std::string& str;
};
private:
std::variant<Sinks...> _manyfold;
};
#pragma once
#include <string>
#include <iostream>
class Sink1
{
public:
void put(const std::string& s)
{
std::cout << "Sink1: " << s << std::endl;
}
};
#pragma once
#include <string>
#include <iostream>
class Sink2
{
public:
void put(const std::string& s)
{
std::cout << "Sink2: " << s << std::endl;
}
};
Loop In The Middle¶
#pragma once
#include "source.h"
#include "sink.h"
template <class Source, class Sink>
class Loop
{
public:
Loop(Source& source, Sink& sink)
: _source(source), _sink(sink) {}
void doit(unsigned ntimes)
{
while (ntimes--) {
std::string s = _source.get(); // <-- visiting std::variant
_sink.put(s); // <-- visiting std::variant
}
}
private:
Source& _source; // <-- std::variant
Sink& _sink; // <-- std::variant
};