Short version of question
I seek advice as to whether to use ./*this versus ->/this,
i.e. C++ (*this).chained().methods() versus this->chained()->methods().
By the way, at the moment most of the pages I have seen recommend
[[C++ (*this).chained().methods()]].
I was just wondering, because you can't do
My_Class object.chained().methods();
(By the way, I have not tested the examples in this first section. I provide tested examples in the second section.)
You must do
My_Class object;
object.chained().methods();
which is an annoying extra line
Or you can do
My_Class object = My_Class().object.chained().methods();
which requires a value copy - not acceptable if the constructor has side effects, like registering the object instance - like so many Knobs libraries do
Or you can do
My_Class* object_ptr = *(new My_Class).object.chained().methods();
which works, but requires that annoying *(ptr)
Or you can do
My_Class* object_ptr = (new My_Class)->object.chained()->methods();
which is a teensy bit better.
I suppose you can do
My_Class& object_ref(My_Class().chained().methods());
and I am not sure what I think about that.
By the way, I do NOT need debugging help here.
I code stuff like this up all the time
I provide the examples only for clarity.
I am seeking style advice, because there are several ways to code it,
and I have used different libraries that do it in opposite ways.
And mixing them is ugly:
My_Object_with_Setters* object_ptr2 = &((new My_Object_with_Setters)->set_R1(1).set_P1(2)->set_R1(3))
My_Object().method_returning_ptr()->method_returning_ref();
Maybe it's not that bad.... but it sure can be confusing.
When I run into code that uses two different libraries using mixed .chained()->methods()
I sometimes wish for the ability to have postfix address-of and dereference operators
My_Object* mptr = My_Object() .method_returning_ptr() -> method_returning_ref ->&
More complete Examples
Setter Functions
I most often use this idiom with setter functions
class My_Object_with_Setters {
public:
static int count;
int value;
public:
My_Object_with_Setters() {
++count;
value = 0;
}
public:
std::ostream& print_to_stream(std::ostream& ostr) const {
ostr << "(" << this->count << "," << this->value << ")";
return ostr;
}
friend std::ostream&
operator<< (
std::ostream& ostr,
const My_Object_with_Setters& obj ) {
return obj.print_to_stream(ostr);
}
public:
My_Object_with_Setters& set_R1(int val) {
this->value = val;
std::cout << "set_R1: " << *this << "\n";
return *this;
}
My_Object_with_Setters& set_R2(int val) {
this->value = val;
std::cout << "set_R2: " << *this << "\n";
return *this;
}
public:
My_Object_with_Setters* set_P1(int val) {
this->value = val;
std::cout << "set_P1: " << *this << "\n";
return this;
}
My_Object_with_Setters* set_P2(int val) {
this->value = val;
std::cout << "set_P2: " << *this << "\n";
return this;
}
public:
My_Object_with_Setters set_V1(int val) {
this->value = val;
std::cout << "set_V1: " << *this << "\n";
My_Object_with_Setters retval;
retval = *this; // kluge to force new object
return retval;
}
My_Object_with_Setters set_V2(int val) {
this->value = val;
std::cout << "set_V2: " << *this << "\n";
My_Object_with_Setters retval;
retval = *this; // kluge to force new object
return retval;
}
};
int My_Object_with_Setters::count = 0; // clas static, distinguishes instances
void test_My_Object_with_Setters()
{
std::cout << "cascading ref, ref, copy, copy, ref, ref\n";
My_Object_with_Setters object;
object.set_R1(1).set_R2(2).set_V1(11).set_V2(12).set_R1(101).set_R2(102);
std::cout << "cascading ptr, ptr, ptr, ptr\n";
My_Object_with_Setters* object_ptr = (new My_Object_with_Setters)->set_P1(1)->set_P2(2)->set_P1(11)->set_P2(12);
std::cout << "cascading &address-of, ptr, ptr\n";
(&object)->set_P1(1)->set_P2(2);
std::cout << "cascading new ptr ref ptr ref\n";
My_Object_with_Setters* object_ptr2 = &(*(new My_Object_with_Setters)->set_R1(1).set_P1(2)).set_R1(3);
}
Test output:
cascading ref, ref, copy, copy, ref, ref
set_R1: (1,1)
set_R2: (1,2)
set_V1: (1,11)
set_V2: (2,12)
set_R1: (3,101)
set_R2: (3,102)
cascading ptr, ptr, ptr, ptr
set_P1: (4,1)
set_P2: (4,2)
set_P1: (4,11)
set_P2: (4,12)
cascading &address-of, ptr, ptr
set_P1: (4,1)
set_P2: (4,2)
cascading new ptr ref ptr ref
set_R1: (5,1)
set_P1: (5,2)
set_R1: (5,3)
Generic Example
class My_Object {
public:
static int count;
public:
My_Object() {
++count;
}
public:
My_Object& method1_returning_ref_to_current_object() {
std::cout << count << ": method1_returning_ref_to_current_object\n";
return *this;
}
My_Object& method2_returning_ref_to_current_object() {
std::cout << count << ": method2_returning_ref_to_current_object\n";
return *this;
}
public:
My_Object* method1_returning_ptr_to_current_object() {
std::cout << count << ": method1_returning_ptr_to_current_object\n";
return this;
}
My_Object* method2_returning_ptr_to_current_object() {
std::cout << count << ": method2_returning_ptr_to_current_object\n";
return this;
}
public:
My_Object method1_returning_value_copy_of_current_object() {
std::cout << count << ": method1_returning_value_copy_of_current_object\n";
My_Object retval;
return retval;
}
My_Object method2_returning_value_copy_of_current_object() {
std::cout << count << ": method2_returning_value_copy_of_current_object\n";
My_Object retval;
return *this;
}
};
int My_Object::count = 0; // clas static, distinguishes instances
void test_My_Object()
{
std::cout << "cascading ref, ref, copy, copy, ref, ref\n";
My_Object object;
object
.method1_returning_ref_to_current_object()
.method2_returning_ref_to_current_object()
.method1_returning_value_copy_of_current_object()
.method2_returning_value_copy_of_current_object()
.method1_returning_ref_to_current_object()
.method2_returning_ref_to_current_object()
;
std::cout << "cascading ptr, ptr, ptr, ptr\n";
My_Object* object_ptr = new My_Object;
object_ptr
->method1_returning_ptr_to_current_object()
->method2_returning_ptr_to_current_object()
->method1_returning_ptr_to_current_object()
->method2_returning_ptr_to_current_object()
;
std::cout << "cascading &address-of, ptr, ptr\n";
(&object)
->method1_returning_ptr_to_current_object()
->method2_returning_ptr_to_current_object()
;
std::cout << "cascading new ptr ref ptr ref\n";
My_Object* object_ptr2
= (&(*(new My_Object)
->method1_returning_ptr_to_current_object())
.method2_returning_ref_to_current_object())
;
}
Test output
cascading ref, ref, copy, copy, ref, ref
1: method1_returning_ref_to_current_object
1: method2_returning_ref_to_current_object
1: method1_returning_value_copy_of_current_object
2: method2_returning_value_copy_of_current_object
3: method1_returning_ref_to_current_object
3: method2_returning_ref_to_current_object
cascading ptr, ptr, ptr, ptr
4: method1_returning_ptr_to_current_object
4: method2_returning_ptr_to_current_object
4: method1_returning_ptr_to_current_object
4: method2_returning_ptr_to_current_object
cascading &address-of, ptr, ptr
4: method1_returning_ptr_to_current_object
4: method2_returning_ptr_to_current_object
cascading new ptr ref ptr ref
5: method1_returning_ptr_to_current_object
5: method2_returning_ref_to_current_object
By the way, I do NOT need debugging help here. I provide the examples only for clarity.
I am seeking style advice.