I am trying to implement builder pattern with fluent interface for building the objects in C++. I want the builder to follow CRTP pattern.
In Java, I would do something similar to the below code. How do I do the same in C++?
The below is some java code that has a base class and a derived class. The derived class's builder inherits the base class's builder..
// Base class
public abstract class BaseClass {
private final int base_class_variable;
BaseClass(final Builder <?> builder) {
this.base_class_variable = builder.base_class_variable;
}
public abstract static class Builder <B extends Builder> {
int base_class_variable;
public B setBaseClassVariable(final int variable) {
this.base_class_variable = variable;
return self();
}
protected abstract B self();
}
}
// Derived class
public final class DerivedClass extends BaseClass {
private final int derived_class_variable;
private DerivedClass(final Builder builder) {
super(builder);
this.derived_class_variable = derived_class_variable;
}
public static Builder builder() {
return new Builder();
}
public static final class Builder extends BaseClass.Builder <Builder> {
private int derived_class_variable;
public Builder setDerivedClassVariable(final int variable) {
this.derived_class_variable = variable;
return self();
}
public DerivedClass build() {
return new DerivedClass(this);
}
#Override
protected Builder self() {
return this;
}
}
}
// Creating an instance of DerivedClass
DerivedClass dInstance = DerivedClass.builder()
.setBaseClassVariable(5)
.setDerivedClassVariable(10)
.build();
Here is one way to do it in C++:
template <typename T>
class Builder {
public:
static T builder() { return {}; }
T & build() {return static_cast<T&>(*this); }
};
template <typename T>
class BaseClass : public Builder<T> {
int base_class_variable;
public:
T& setBaseClassVariable(int variable) {
base_class_variable = variable;
return static_cast<T&>(*this);
}
};
class DerivedClass : public BaseClass<DerivedClass> {
int derived_class_variable;
public:
DerivedClass& setDerivedClassVariable(int variable) {
derived_class_variable = variable;
return *this;
}
};
int main()
{
// Creating an instance of DerivedClass
DerivedClass dInstance = DerivedClass::builder()
.setBaseClassVariable(5)
.setDerivedClassVariable(10)
.build();
}
Here is an example that will only allow the values to be changed on an rvalue reference (as returned by the builder):
#include <utility>
template <typename T>
class Builder {
public:
static T builder() { return {}; }
T & build() {return static_cast<T&>(*this); }
};
template <typename T>
class BaseClass : public Builder<T> {
int base_class_variable;
public:
T&& setBaseClassVariable(int variable) && {
base_class_variable = variable;
return std::move(static_cast<T&>(*this));
}
};
class DerivedClass : public BaseClass<DerivedClass> {
int derived_class_variable;
public:
DerivedClass&& setDerivedClassVariable(int variable) && {
derived_class_variable = variable;
return std::move(*this);
}
};
int main()
{
// Creating an instance of DerivedClass
DerivedClass dInstance = DerivedClass::builder()
.setBaseClassVariable(5)
.setDerivedClassVariable(10)
.build();
//dInstance.setBaseClassVariable(34); // will not compile
}
Here is a third solution that uses a Proto class which is returned by the builder(). The private member functions have to be specified with using statements to that they can be made available for public use by Proto. Finally the build() function returns the DerivedClass which does not expose the member functions.
template<typename T>
class BaseClass;
class DerivedClass;
template <typename T>
class Proto : public T {
public:
using BaseClass<T>::setBaseClassVariable;
using T::setDerivedClassVariable;
};
template <typename T>
class Builder {
public:
static Proto<T> builder() { return {}; }
T& build() { return static_cast<T&>(*this); }
};
template <typename T>
class BaseClass : public Builder<T> {
int base_class_variable;
Proto<T>& setBaseClassVariable(int variable) {
base_class_variable = variable;
return static_cast<Proto<T>&>(*this);
}
friend class Proto<T>;
};
class DerivedClass : public BaseClass<DerivedClass> {
int derived_class_variable;
Proto<DerivedClass>& setDerivedClassVariable(int variable) {
derived_class_variable = variable;
return static_cast<Proto<DerivedClass>&>(*this);
}
friend class Proto<DerivedClass>;
};
int main()
{
// Creating an instance of DerivedClass
DerivedClass dInstance = DerivedClass::builder()
.setBaseClassVariable(5)
.setDerivedClassVariable(10)
.build();
//dInstance.setBaseClassVariable(34); // cannot access private member
}
This approach might inspire something better, so I think it should be shared.
First create a class for the members you want to supply using the builder pattern, lets call it the members class and the immutable class to construct the builder class.
The members class will used for:
The builder class will inherit from it.
The builder class accepts it in its constructor, for supplying all the const values for the const members.
Now we want to create an fluent interface for setting the member variables on the members class.
A conflict appears: To make the builder class members const the members class also needs to have them const.
But fluent construction requires a way of giving the arguments one at a time and ideally a way to control what order it is possible to give the arguments in.
Example:
We have a class representing a running process, to construct it we need to know:
1.(Command) What command to execute
2.(Mode) Will only reading from the stdout be required(read mode) or will it be used interactively requiering the ability to write to its stdin(write mode).
3.(Target) Where shall the stdout be redirected to? cout, a file or a pipe?
For simplicity all the arguments will be represented by strings.
Limiting the valid methods after each provided argument is great for autocompletion, but it requires us to define a scope with the valid methods
and what scope it will transition to- for each stage in the construction.
Perhaps a type dependent namespace would be better, but I wanted to reuse the members class if possible.
Each argument interface is represented by a class with method(s) for supplying a constructor argument.
The method will return an object having the next interface as its type for supplying the next constructor argument or the finished builder object.
I reuse the same object for all construction stages but the interface changes by static casting.
We start by creating the last interface the client will use before the builder class will be constructed, in this case (3) the target argument.
Lets name if after that:
struct Target : protected members_class
{
builder_class havingTarget( const string& _target )
{
this->target = target;
return builder_class ( *(this) ) ;
}
};
The builder class is constructable by giving it an members_class object, we inherit from the members_class, so we can return a constructed builder class by supplying the this pointer.
Before the target interface we have the interface for setting the mode argument:
struct Mode : protected Target
{
Target& inMode( const string& mode )
{
this->mode = mode;
return static_cast<Target&>(*this);
}
};
Mode inherits from target, for switching to the target interface after supplying the mode argument, we cast the this pointer to the target interface.
Last the Command interface:
struct Command : protected Mode
{
Mode& withCommand( const string& command )
{
this->command = command;
return static_cast<Mode&>(*this);
}
};
Inheriting from mode and returning a this pointer casted to the mode type after taking the command argument.
But we have a conflict, the members class is used by the builder class for inhereting the members and we want them to be const.
But the builder pattern uses the member class in a way where each argument is provided one at a time.
struct members_class
{
string target;
string mode;
string command;
};
First lets enable a way to supply a template argument that will decide if the members will be const or not:
template <typename T>
using noop = T;
template< template <typename> class constner = noop >
struct members_dyn_const
By default the argument is a no operation, but if std::remove_const_t is supplied the members will not be const as they are declared like this:
constner<const string> target;
constner<const string> mode;
constner<const string> command;
two aliases for the two ways of creating the class:
using members = members_dyn_const<>;
using members_mutable = members_dyn_const<std::remove_const_t>;
Now we want to enable construction of a const members class with a mutable member class:
template< template <typename> class C>
members_dyn_const( members_dyn_const<C> m) : target(m.target), mode(m.mode), command(m.command){}
But we also needs to define default values for the members when it is constructed as a mutable class:
members_dyn_const () : target(""), mode(""), command(""){}
Now we define the builder class inhereting from the const members class, but accepting an mutable members class for construction of the const:
class base_process : protected members
{
public:
base_process( members_mutable _members ) : members( _members ) {}
Now we can construct a builder class with:
process_builder.withCommand( "ls" ).inMode( "read" ).havingTarget( "cout" );
and an immutable class is created with const members.
I havent seen this approach described anywhere else, so I wanted to share it as it might provide inspiration for a better way, but I cant really recommend it and I havent really tested or polished the code beyond a proof of concept.
#include <string>
#include <iostream>
using namespace std;
namespace process
{
namespace details
{
template <typename T>
using noop = T;
template< template <typename> class constner = noop >
struct members_dyn_const
{
friend class members_dyn_const< noop >;
template< template <typename> class C>
members_dyn_const( members_dyn_const<C> m) : target(m.target), mode(m.mode), command(m.command){}
members_dyn_const () : target(""), mode(""), command(""){}
protected:
constner<const string> target;
constner<const string> mode;
constner<const string> command;
};
using members = members_dyn_const<>;
using members_mutable = members_dyn_const<std::remove_const_t>;
namespace builder
{
class base_process : protected members
{
public:
base_process( members_mutable _members ) : members( _members ) {}
void test() { /*command = "X";*/ cout << "Executing command: " << command << " in mode " << mode << " having target " << target << endl; }
};
namespace arguments
{
struct Target : protected members_mutable
{
base_process havingTarget( const string& _target )
{
this->target = target;
return base_process( *(this) ) ;
}
};
struct Mode : protected Target
{
auto& inMode( const string& mode )
{
this->mode = mode;
return static_cast<Target&>(*this);
}
};
struct Command : protected Mode
{
Mode& withCommand( const string& command )
{
this->command = command;
return static_cast<Mode&>(*this);
}
};
}
}
}
using details::builder::base_process;
using details::builder::arguments::Command;
Command process_builder = Command();
}
using namespace process;
int main()
try
{
process_builder.withCommand( "ls" ).inMode( "read" ).havingTarget( "cout" ).test();
return 0;
}
catch( exception& e )
{
cout << "ERROR:" << e.what() << endl;
return -1;
}
https://onlinegdb.com/BySX9luim
Related
My goal is to construct a derived classes nested class from the interface. However the nested classes don't have the same constructors. The question is how can I make an interface to create two different "sub-nested" classes.
Constraints:
Cannot use Heap
Nested Classes' Methods cannot be called before it is constructed
C++ 17
ITest::INestedTest* MakeTest(ITest* test, ITest::Config config)
{
// Can't call directly because it's not on the interface i.e. test.InitializeNestedTest ...
// Only workable situation is this:
if (condition)
{
auto myTest = static_cast<Test2::Test*>(test);
int p = 2;
return myTest->InitalizeNestedTest(config, p);
// ERROR function returning abstract class not allowed
} else {
auto myTest = static_cast<Test1::Test*>(test);
return myTest->InitalizeNestedTest(config);
// ERROR function returning abstract class not allowed
}
}
This static cast didn't return what I wanted previously because I was returning a pointer to a locally defined variable, which was pointed out in the comments. How am I able to return a class from this since it's an abstract class, do i need to cast it again or make multiple functions?
Test1::Test myTest;
auto myNestedTest = myTest.InitializeNestedTest(config);
I've thought of a few options but none of them seem right, or I'm not entirely sure how to implement them
Have an overloaded Virtual function for each type on the interface and then override them on the subclass (not sure if possible and doesn't seem like the right way to do it)
Extend the Config struct Test2 namespace so that it includes parameter p, so that they all have the same prototype and put it on the interface. (is it possible to "extend" the struct" from the interface?)
Maybe use a different type of cast, or do so in a different way?
I've included the definitions of my Interface and two subclasses for reference.
class ITest
{
//other things in ITest.hpp not relevant to question
public:
struct Config
{
int a;
bool enable;
};
class INestedTest
{
public:
virtual void Enable() const = 0;
virtual void Configure(Config const& config)
{
if(config.enable)
{
Enable();
}
}
};
};
namespace Test1
{
class Test : public ITest
{
public:
class NestedTest : public ITest::INestedTest
{
public:
NestedTest(Config const& config)
{
Configure(config);
}
void Enable() const override
{
//impl
}
}; // End NestedTest
NestedTest InitalizeNestedTest(Config const& config)
{
return NestedTest(config);
}
};
};
namespace Test2
{
class Test : public ITest
{
public:
class NestedTest : public ITest::INestedTest
{
public:
using Parameter = int;
NestedTest(ITest::Config const& config, Parameter p)
{
Configure(config);
}
void Enable() const override
{
//impl
}
}; // End NestedTest
NestedTest InitalizeNestedTest(Config const& config, NestedTest::Parameter p)
{
return NestedTest(config, p);
}
};
};
Maybe you could make the object static so it's declared in RAM at compile time (and not heap or stack).
I have a Logging class that is inherited by LoggingString, LoggingInt, etc. which each have a writeLog function that takes in the corresponding type:
class Logging {
public:
explicit Logging(const LoggingConfig& config);
...
}
class LoggingString : public Logging {
public:
using Logging::Logging;
writeLog(std::string str);
...
}
In the code where I use LoggingString, I want to be able to call a factory function like this:
std::unique_ptr<LoggingString> logger = LoggingString::CreateIfLoggingEnabled(LoggingConfig{...});
std::unique_ptr<LoggingInt> logger = LoggingInt::CreateIfLoggingEnabled(LoggingConfig{...});
Considering they're all using the same constructor derived from the base Logging class, is it possible to put this static Logging::CreateIfLoggingEnabled function in the base class and somehow have it inherited by the Logging derived classes, but returning a unique_ptr<LoggingDerivedClass> instead? Maybe something with templating like this:
class Logging {
public:
explicit Logging(const LoggingConfig& config);
template<typename T> static std::unique_ptr<T> CreateIfLoggingEnabled(const LoggingConfig& config) {
...
return std::unique_ptr<T>{};
}
...
}
std::unique_ptr<LoggingString> logger = LoggingString::CreateIfLoggingEnabled<LoggingString>(LoggingConfig{...});
Not sure if this would work, and besides it seems redundant to have to specify LoggingString twice. Any other options?
it seems redundant to have to specify LoggingString twice.
You wouldn't have to specify LoggingString twice (thrice if you count the logger variable declaration). You can replace LoggingString:: with Logging::, and use auto for the variable, eg:
class Logging {
public:
explicit Logging(const LoggingConfig& config);
template<typename T>
static std::unique_ptr<T> CreateIfLoggingEnabled(const LoggingConfig& config) {
...
return std::make_unique<T>(config);
}
...
};
auto logger = Logging::CreateIfLoggingEnabled<LoggingString>(LoggingConfig{...});
auto logger = Logging::CreateIfLoggingEnabled<LoggingInt>(LoggingConfig{...});
Demo
Alternatively, you can use CRTP to pass the derived type into the base class, eg:
template<typename Derived>
class Logging {
public:
explicit Logging(const LoggingConfig& config);
static std::unique_ptr<Derived> CreateIfLoggingEnabled(const LoggingConfig& config) {
...
return std::make_unique<Derived>(config);
}
...
};
class LoggingString : public Logging<LoggingString> {
public:
using Logging<LoggingString>::Logging;
void writeLog(std::string str);
...
};
class LoggingInt : public Logging<LoggingInt> {
public:
using Logging<LoggingInt>::Logging;
void writeLog(int i);
...
};
auto logger = LoggingString::CreateIfLoggingEnabled(LoggingConfig{...});
auto logger = LoggingInt::CreateIfLoggingEnabled(LoggingConfig{...});
Demo
I want to design a component-based weapon template for my game. However, it seems no way to add/remove a class member or create a code?
Sorry for my expression and lack of terminology, for I am not graduated from dept. of computer science or software engineer, I know little of what those stuff called by professionals.
Here is the component code looks like:
class CBaseWpnCmpt : public std::enable_shared_from_this<CBaseWpnCmpt>
{
public:
typedef std::shared_ptr<CBaseWpnCmpt> PTR;
private:
CBaseWpnCmpt() = default;
public:
CBaseWpnCmpt(const CBaseWpnCmpt& s) = default;
CBaseWpnCmpt(CBaseWpnCmpt&& s) = default;
CBaseWpnCmpt& operator=(const CBaseWpnCmpt& s) = default;
CBaseWpnCmpt& operator=(CBaseWpnCmpt&& s) = default;
virtual ~CBaseWpnCmpt() {}
protected:
CBaseWeaponInterface::PTR m_pWeapon { nullptr };
public:
template <class CComponent>
static std::shared_ptr<CComponent> Create(CBaseWeaponInterface::PTR pWeapon)
{
std::shared_ptr<CComponent> pComponent = std::make_shared<CComponent>();
pComponent->m_pWeapon = pWeapon;
return pComponent;
}
};
And this is what a weapon body code looks like: (And the problem occurs)
template < class CWeapon,
class ...CComponents
>
class CBaseWeaponTemplate : public CBaseWeaponInterface
{
public:
std::list<CBaseWpnCmpt::PTR> m_lstComponents;
public:
virtual void SecondaryAttack(void) // Example method.
{
for (auto& pComponent : m_rgpComponents)
{
pComponent->SecondaryAttack();
}
}
};
How am I suppose to create all these argument packs as member of the template? Currently I tried to enlist them into a pointer std::list container, but I just can't figure out how to achieve it at all.
In other words, how can I make a template when I fill in blank likt this:
class CAK47 : public CBaseWeaponTemplate<CAK47, CLongMagazine, CWoodenStock>
will generate this:
class CAK47
{
CLongMagazine m_comp1;
CWoodenStock m_comp2;
//... other stuff
};
Or alternatively, generate this:
class CAK47
{
CAK47() // constructor
{
for (/* somehow iterate through all typenames */)
{
CBaseWpnCmpt::PTR p = std::make_shared<typename>();
m_lstComponents.emplace_back(p);
}
}
};
One way of doing so from C++11 on-wards would be to store the template types used for this particular weapon inside an std::tuple
template <typename Weapon, typename... Attachments>
class WeaponWithAttachments {
protected:
WeaponWithAttachments() {
return;
}
std::tuple<Attachments...> attachment_types;
};
and then using that tuple to initialise a vector of shared pointers with a protected constructor taking a tuple to access the template types again.
class SomeWeaponWithAttachments: public WeaponWithAttachments<SomeWeapon,SomeAttachment,AnotherAttachment> {
public:
SomeWeaponWithAttachments()
: SomeWeaponWithAttachments{attachment_types} {
return;
}
protected:
template <typename... Attachments>
SomeWeaponWithAttachments(std::tuple<Attachments...> const&)
: attachments{std::make_shared<Attachments>()...} {
return;
}
std::vector<std::shared_ptr<BaseAttachment>> attachments;
};
Try it here!
If the attachments vector is already declared inside the parent class like it seems to be the case for you might also avoid the tuple and the protected constructor with initialising the attachments already inside the parent class
template <typename Weapon, typename... Attachments>
class WeaponWithAttachments {
protected:
WeaponWithAttachments()
: attachments{std::make_shared<Attachments>()...} {
return;
}
std::vector<std::shared_ptr<BaseAttachment>> attachments;
};
and then only calling the constructor of the base class in the derived class
class SomeWeaponWithAttachments: public WeaponWithAttachments<SomeWeapon,SomeAttachment,AnotherAttachment> {
public:
SomeWeaponWithAttachments()
: WeaponWithAttachments<SomeWeapon,SomeAttachment,AnotherAttachment>() {
return;
}
};
Try it here!
If that is no option for you, then you can use the tuple to iterate over all the template arguments using C++17 fold expressions:
class SomeWeaponWithAttachments: public WeaponWithAttachments<SomeWeapon,SomeAttachment,AnotherAttachment> {
public:
SomeWeaponWithAttachments()
: SomeWeaponWithAttachments{attachment_types} {
return;
}
protected:
template <typename... Attachments>
SomeWeaponWithAttachments(std::tuple<Attachments...> const&) {
(attachments.push_back(std::make_shared<Attachments>()), ...);
return;
}
};
Try it here!
In C++17 you might also add a static assertion with fold expressions into the constructor to make sure that the types actually inherit from BaseAttachment:
static_assert((std::is_base_of_v<BaseAttachment, Attachments> && ...), "Template arguments must inherit from 'BaseAttachment'.");
In my program, I have an Object class to which we can attach components that all derive from a base Component class. Since a component can have data that are initialized through it's constructor, when we call Object::addComponent() we need to pass the data for this particular component
#include <vector>
class Component;
class Object
{
public:
Object() {}
/* The challenge comes from implementing this function, the signature will change later in this post
and ideally it would return the added component */
void addComponent();
private:
std::vector<Component*> m_components;
};
class Component
{
public:
Component(Object* owner) : m_owner(owner) {}
// Note the pure virtual destructor, meaning Component is meant to be derived
virtual ~Component() = 0;
private:
Object* m_owner;
};
Here are two Component derived classes for our example
// This component holds an int
class ComponentDerivedA : public Component
{
public:
ComponentDerivedA(Object* owner, int data) : Component(owner), m_data(data) {}
virtual ~ComponentDerivedA() override {}
private:
int m_data;
};
// This component holds a string
class ComponentDerivedB : public Component
{
public:
ComponentDerivedB(Object* owner, char* message) : Component(owner), m_message(message) {}
virtual ~ComponentDerivedB() override {}
private:
char* message;
};
The only solution I came for to implement the addComponent() function the way I want to is to create an enum and a struct:
enum class ComponentType { A, B };
struct ComponentInfo
{
ComponentType type;
int data;
char* message
};
So we change the signature for Object::addComponent() to the following
void* Object::addComponent(const ComponentInfo& info);
And here is a possible implementation
void* Object::addComponent(const ComponentInfo& info)
{
switch(info.type)
{
case A:
{
// We ignore cleaning up for this example but it would go in the destructor
ComponentDerivedA* a = new ComponentDerivedA(this, info.data);
m_components.push_back(a);
return a;
}
break;
case B:
{
// We ignore cleaning up for this example but it would go in the destructor
ComponentDerivedB* b = new ComponentDerivedB(this, info.message);
m_components.push_back(b);
return b;
}
break;
}
}
And here is how we would use it
int main()
{
Object obj;
ComponentInfo info{0};
info.type = ComponentType::A;
info.data = 5;
obj.addComponent(info);
/*
If I wanted to use the component right after I would have to do:
reinterpret_cast<ComponentDerivedA>(obj.addComponent(info)).doSomething();
*/
return 0;
}
This solution works okay but anytime you want to call addComponent() you have to declare a struct before, the only benefit is if you add multiple components you can reuse the same struct and just change the data between each call, also is if you add a new component type you have to expand the enum and the function, if you have many component the switch can become very large but the code inside it remains pretty repetitive and simple. And the final flaw is that it's up to the caller to cast the return value as the component type.
Here is a possible solution.
Note the use of std::unique_ptr instead of regular c-pointers.
#include <memory>
#include <vector>
struct Component{};
class Object {
public:
Object() {}
template <typename TDerivedComponent, typename... TArgs>
TDerivedComponent * addComponent(TArgs&&... args){
auto ptr = std::make_unique<TDerivedComponent>
(std::forward<TArgs>(args)...);
TDerivedComponent * result = ptr.get();
m_components.push_back(std::move(ptr));
return result;
}
private:
std::vector<std::unique_ptr<Component> > m_components;
};
struct ComponentA : public Component {
ComponentA(int x, int y){}
};
struct ComponentB : public Component {};
struct Invalid{};
int main(){
Object obj;
ComponentA * a = obj.addComponent<ComponentA>(1, 2);
ComponentB * b = obj.addComponent<ComponentB>();
// obj.addComponent<Invalid>(); -> error!
}
You actually can store a derived object into a baseclass object pointer which u already have declared as in the vector.
To make your derived objects also call the correct methods you have to declare the method as virtual inside the base class.
your addComponent() function can then take the pointer of the baseClass
addComponent(Component * c)
with that signature it can also take derived object pointers.
You should read about static vs dynamic binding in C++.
Suppose I have two classes...
We can call the first FooReader and it looks something like this:
class FooReader {
public:
FooReader(const Foo* const foo)
: m_foo(foo) {
}
FooData readFooDataAndAdvance() {
// the point here is that the algorithm is stateful
// and relies upon the m_offset member
return m_foo[m_offset++];
}
private:
const Foo* const m_foo;
size_t m_offset = 0; // used in readFooDataAndAdvance
};
We can call the second FooWriter and it looks something like this:
class FooWriter {
public:
FooWriter(Foo* const foo)
: m_foo(foo) {
}
void writeFooDataAndAdvance(const FooData& foodata) {
// the point here is that the algorithm is stateful
// and relies upon the m_offset member
m_foo[m_offset++] = foodata;
}
private:
Foo* const m_foo;
size_t m_offset = 0;
};
These both work wonderfully and do their job as intended. Now suppose I want to create a FooReaderWriter class. Note that the
I naturally want to say that this new class "is a" FooReader and "is a" FooWriter; the interface is simply the amalgamation of the two classes and the semantics remain the same. I don't want to reimplement perfectly good member functions.
One could model this relationship using inheritance like so:
class FooReaderWriter : public FooReader, public FooWriter { };
This is nice because I get the shared interface, I get the implementation and I nicely model the relationship between the classes. However there are problems:
The Foo* member is duplicated in the base classes. This is a waste of memory.
The m_offset member is separate for each base type, but they need to share it (i.e. calling either readFooDataAndAdvance and writeFooDataAndAdvance should advance the same m_offset member).
I can't use the PIMPL pattern and store m_foo and m_offset in there, because I'd lose the const-ness of the m_foo pointer in the base FooReader class.
Is there anything else I can do to resolve these issues, without reimplementing the functionality contained within those classes?
This seems ready made for the mixin pattern. We have our most base class which just declares the members:
template <class T>
class members {
public:
members(T* f) : m_foo(f) { }
protected:
T* const m_foo;
size_t m_offset = 0;
};
and then we write some wrappers around it to add reading:
template <class T>
struct reader : T {
using T::T;
Foo readAndAdvance() {
return this->m_foo[this->m_offset++];
};
};
and writing:
template <class T>
struct writer : T {
using T::T;
void writeAndAdvance(Foo const& f) {
this->m_foo[this->m_offset++] = f;
}
};
and then you just use those as appropriate:
using FooReader = reader<members<Foo const>>;
using FooWriter = writer<members<Foo>>;
using FooReaderWriter = writer<reader<members<Foo>>>;
CRTP.
template<class Storage>
class FooReaderImpl {
public:
FooData readFooDataAndAdvance() {
// the point here is that the algorithm is stateful
// and relies upon the m_offset member
return get_storage()->m_foo[get_storage()->m_offset++];
}
private:
Storage const* get_storage() const { return static_cast<Storage const*>(this); }
Storage * get_storage() { return static_cast<Storage*>(this); }
};
template<class Storage>
class FooWriterImpl {
public:
void writeFooDataAndAdvance(const FooData& foodata) {
// the point here is that the algorithm is stateful
// and relies upon the m_offset member
get_storage()->m_foo[get_storage()->m_offset++] = foodata;
}
private:
Storage const* get_storage() const { return static_cast<Storage const*>(this); }
Storage * get_storage() { return static_cast<Storage*>(this); }
};
template<class T>
struct storage_with_offset {
T* m_foo = nullptr;
std::size_t m_offset = 0;
};
struct FooReader:
FooReaderImpl<FooReader>,
storage_with_offset<const Foo>
{
FooReader(Foo const* p):
storage_with_offset<const Foo>{p}
{}
};
struct FooWriter:
FooWriterImpl<FooWriter>,
storage_with_offset<Foo>
{
FooWriter(Foo* p):
storage_with_offset<Foo>{p}
{}
};
struct FooReaderWriter:
FooWriterImpl<FooReaderWriter>,
FooReaderImpl<FooReaderWriter>,
storage_with_offset<Foo>
{
FooReaderWriter(Foo const* p):
storage_with_offset<Foo>{p}
{}
};
If you need an abstract interface for runtime polymorphism, inherit FooReaderImpl and FooWriterImpl from them.
Now, FooReaderWriter obeys the ducktype contract of FooReader and FooWriter. So if you use type erasure instead of inheritance, it will qualify for either (at point of use).
I'd be tempted to change them to
using FooReader = std::function<FooData()>;
using FooWriter = std::function<void(FooData const&)>;
and then implement a multi-signature std::function for FooReaderWriter. But I'm strange and a bit unhinged that way.