Consider the following code base design pattern:
template<typename T>
class Foo {
public:
Foo(T t) : class_member_{t} {}
const T class_member() const { return class_member_;}
Foo(const Foo& other ) {
class_member_ = other.class_member_;
}
Foo& operator=(const Foo& other) {
class_member = other.class_member_;
return *this;
}
template<template<class> class U, typename V>
friend auto extend(U<V>& u, V v);
void update_from_hidden() {
class_member_ = hidden_member_;
}
protected:
T hidden_member_{class_member_ + 1};
private:
T class_member_;
void update_class_member(T new_val) { class_member_ = new_val;}
};
template<template<class> class T, typename U>
auto extend(T<U>* tt, U val) {
auto impl = [] (T<U>* t, U val) {
t->hidden_member_ += 5; // okay I want this lambda to be able to
// modify Foo::hidden_member
t->class_member_ = 7; // Invalid, I don't want this lambda to have
// access to Foo's private members
return *t;
};
}
int main() {
Foo<int> foo(3);
foo = extend(&foo, 2);
return foo.class_member();
}
Now, these two lines within the body of the lambda and this line from main:
t->hidden_member_ += 5;
t->hidden_member_ = 7;
// main
foo = extend(&foo, 2);
Will not compile, because these members are protected and private within this context, and operator=()
can't resolve the value 2
.
While trying to keep this code structure, I want lambdas of these type that are through friend functions of some arbitrary class object to be able to have direct access to the class's protected members while still restricting them from the class's private members both variables and private functions.
Can this be achieved, and if so, what would I minimally have to do to this class and or lambda to achieve this behavior?
If not, is there another existing code pattern that would fit this model?
I want to abstract specific methods to outside of the classes, but I prefer to use lambda's within this context. In fact I would have preferred to have something like this instead, but this isn't valid C++ code that I know of...
class Bar {
public:
Bar(int val) : member_{val} {
protected:
int hidden_;
private:
int member_;
};
auto extend = [](Bar* bb, int val) {
friend class Bar; // not valid or legal as far as I know
bb->hidden_ += 3; // I want this to be valid
bb->member_ = 5; // I want this to be blocked, not valid
return *bb;
};
This is where I hit my road block... It's not the implementation of the methods that I'm concerned about, it's how class Foo
and lambda extend
integrate with each other through friend or some other mechanism within the manner and context that I described above... not sure what to do here.
-Edit-
Okay, here is a use case scenario.
Let's say I have a WaterPipe
class that has private members that can only be changed within the class itself. It has some update method to change that private member internally. Now, it has protected members that are not accessible to outside of the class, but want to expose them to lambda's that will modify this class's dimensions or state, but not it's internal properties or behavior.
class WaterPipe {
public:
WaterPipe(float length, float diameter)
: length_{length}, diameter_{diameter}, has_changed{false}
{
calculate_props();
}
friend extend(WaterPipe* pipe, float newLength, float newDiameter);
const auto length() const { return length_; }
const auto diameter() const { return diameter_; }
const auto volume() const { return volume_flow_; }
const auto direction() const { return direction_flow_; }
const auto pressure() const { return pressure_flow_; }
protected:
float length_;
float diameter_;
void recalculate() { calculate_pops(); }
private:
float volume_flow_;
float direction_flow_;
float pressure_flow_;
void calculate_props() {
// implementation here
}
};
auto extend_water_pipe(WaterPipe* pPipe, float newLength, float newDiameter) {
bool changed = false;
auto impl = [](WaterPipe* pipe, float newLength, float newDiameter)
if (pipe->length_ != newLength || pipe->length_ != newDiameter) {
pipe->length_ = newLength;
pipe->diameter_ = newDiameter;
pipe->recalculate();
changed = true;
}
return pipe;
};
if ( !changed ) { return pPipe; }
return impl(pPipe, newLength, newDiamter)();
}
In some GUI Application:
void object_construction_in_app(...) {
// user grabs water pipe drawing tool
// user clicks point on graph node and holds mouse down
// drags it in some direction, stops and releases the mouse
// this calls the constructor on the pipe giving it the length
// the diameter was already set via the `water pipe drawing tool`
// create a new pipe object here with specified values
// and store it into some container to be rendered.
}
// some frames later
void object_update(...) {
// user selects already constructed pipe
// and decides to either change it's diameter through context menu
// or changes its length by clicking on one of its ends and starts
// to drag it again...
// get object from container and use `friend function - lambda`
// to update it's dimensions, return back that object to have
// it be rendered, store it into some other container, such as
// a queue, etc...
}
The Water Pipe will calculate it's own internal values... The external friend lambda, will access it's protected members only by resetting, the length or diameter and invoking the protected recalculate()
function which then invokes the class's internal private calculate_props()
function. The lambda then returns either a pointer to the class object, or returns a deference pointer to the class to allow a copy or assignment to be done.