Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Builder pattern with Fluent interface

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();
like image 372
Abee Avatar asked Jan 03 '23 09:01

Abee


1 Answers

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
}
like image 63
wally Avatar answered Jan 04 '23 21:01

wally