Replace member assignement with methods - c++

After having produced some code in C++, it may be necessary to change the access to a member of a structure or class to something that produces some side effects. In that sense we would need to overload the assignement of a member to something different.
Struct A{
int v;
}
int main(){
A a;
a.v=17;
}
Is there the possibility to do it somehow ?
If there is not the possibility how would had been written the code in order to allow the flexibility to change a member into something more ?
The possibility of having each access to a memeber divided into a getter and setter seems coumbersome and impractical for any reasonable use.

Yes, use a proxy:
struct A
{
v_proxy v;
private:
struct v_proxy
{
v_proxy( int vv = 0 ) : v{ vv }
{}
//Write access
v_proxy& operator=( int i )
{
//Put your new code here
return v = i;
}
//Read access
operator int() const
{
return v;
}
int v;
};
};
int main()
{
A a;
a.v = 0;
};
Writting a generic proxy like this to allow the customization of read/writes in a common non- get/set syntax is easy.
EDIT: Some claim that this doesn't mimic correctly the behaviour of a C# property since in C# we can access this from the properties. Ok, just add a reference to the object and pass it to the proxy ctor. And don't forget to make the proxy class a friend of your class, to give that this reference full access:
class A
{
A() : v{ *this }
{}
friend struct v_proxy
{
v_proxy( A& ref , int vv = 0 ) : v{ vv } , This{ std::ref( ref )
{}
//Write access
int& operator=( int i )
{
//Put your new code here, for example:
This.foo();
return v = i;
}
//Read access
operator int() const
{
return v;
}
int v;
private:
std::reference_wrapper<A> This;
};

Related

Struct with array that changes dynamically

I have been looking to change dynamically the values of an array in a struct depending on other variables of the struct.
Let's say I have:
struct foo
{
int value1 = 0;
int value2 = 0;
int arr[2] = {value1, value2};
};
In the main if I have create an instance fooInstance and I want to associate a value to value1 fooInstance.value1 = 10, how can I update the value in the array ?
Thank you for your time.
Firstly, if you need an array, then I recommend storing the objects in the array directly.
I question the value (i.e. usefulness) of these aliases such as value1 when the name has no more meaning than referring to arr[i] directly. But I can see the value in case there is a descriptive name available. I'll use a more meaningful example of 2D vector with x, y dimensions. It should be easy to change float to int and change the names to match your attempt.
While Frank's solution using functions is great in most regards, it has a small caveat of having a less convenient syntax compared to variables. It's possible to achieve the variable syntax using operator overloading and anonymous unions. The trade-off is the increased boilerplate in the class definition. Example:
union Vector2 {
struct {
float a[2];
auto& operator=(float f) { a[0] = f; return *this; }
operator float&() & { return a[0]; }
operator const float&() const & { return a[0]; }
operator float () && { return a[0]; }
float* operator&() { return &a[0]; }
} x;
struct {
float a[2];
auto& operator=(float f) { a[1] = f; return *this; }
operator float&() & { return a[1]; }
operator const float&() const & { return a[1]; }
operator float () && { return a[1]; }
float* operator&() { return &a[1]; }
} y;
struct {
float a[2];
auto& operator=(float f) { a[0] = a[1] = f; return *this; }
float* begin() { return std::begin(a); }
float* end() { return std::end(a); }
} xy;
};
int main() {
Vector2 v2;
v2.xy = 1337; // assign many elements by name
v2.x = 42; // assign one element by name
std::cout << v2.x; // read one element by name
for(float f : v2.xy) { // iterate the entire array
std::cout << f;
}
}
Note to those unfamiliar with rules of unions: Reading from inactive union member is allowed only through common initial sequence of standard layout structs. This code is well defined, but the reader should be careful to not over generalise and assume that type punning through unions would be allowed; It isn't.
I adapted code from my earlier answer to another question.
It is different parameters coming from different hardwares.
This sounds like generating the accessors shown above with meta programming could be a good approach.
But, if you would like to avoid the complexity, then a more traditional approach would be to just use the array, and use enum to name the indices:
struct foo
{
int arr[100];
enum indices {
name1,
name2,
// ...
name100,
name_count,
};
};
int main()
{
foo f;
f.arr[foo.name1] = 42;
}
If at all possible, use encapsulation. That's the preferred way to create an interface/implementation skew:
struct foo
{
int& value1() { return arr_[0]; }
int& value2() { return arr_[1]; }
int* arr() { return arr_; }
private:
int arr_[2] = {0, 0};
};
void bar(foo& v) {
// access a single value
v.value1() = 3;
// access the whole array
v.arr()[0] = 5;
}
If you need access through both the individual member variables and through an array member variable, do not copy the data; rather, use the array as "the source of truth", and provide access through the individual variables or the individual member functions.
Here is your example rewritten to "alias" array variables to scalar member variables:
struct foo
{
foo() : value1(arr[0]), value2(arr[1]) {}
std::array<int,2> arr;
int& value1;
int& value2;
};
Note: this is not a good way of doing anything in production code, just an illustration of how the language lets you do something like this. Normally I would add accessor member-functions instead of member-variable references, because it avoids many problems referenced in the comments, such as breaking the value semantics.

Can you write a copy constructor for a union with const members?

Suppose I have a struct that contains a union with const members, like so:
struct S
{
// Members
const enum { NUM, STR } type;
union
{
const int a;
const std::string s;
};
// Constructors
S(int t_a) : type(NUM), a(t_a);
S(const std::string & t_s) : type(STR), s(t_s);
};
So far, so good. But now say I want to write a copy-constructor for this type.
It doesn't seem like this involves doing anything nefarious, but since I need to initialize the const members in member initializers I don't see how to do this based on logic that depends on the type member.
Questions:
Is it possible to write this constructor?
If not, is this essentially a syntactic oversight, or is there some fundamental reason that the language can't support such a thing?
Yes, it is possible to write copy constructor here. Actually it is already done inside std::variant implementation, which shall support const-types among others. So your class S can be replaced with
using S = std::variant<const int, const std::string>;
But if for dome reason you cannot use std::variant then copy-constructor can be written using std::construct_at function as follows:
#include <string>
struct S {
const enum { NUM, STR } type;
union {
const int a;
const std::string s;
};
S(int t_a) : type(NUM), a(t_a) {}
S(const std::string & t_s) : type(STR), s(t_s) {}
S(const S & rhs) : type(rhs.type) {
if ( type == NUM ) std::construct_at( &a, rhs.a );
if ( type == STR ) std::construct_at( &s, rhs.s );
}
~S() {
if ( type == STR ) s.~basic_string();
}
};
int main() {
S s(1);
S u = s;
S v("abc");
S w = v;
}
Demo: https://gcc.godbolt.org/z/TPe8onhWs

how to forbid assignment to not reference variables?

I fear it's a dumb question but...
Someone can suggest me a way to force that a return value from a function (or a method), that return a reference to an internal static variable or a member of the class/struct, is assigned only to reference variables ?
I try to explain what I desire with a minimal example.
Given the following code, with a function wrapValue() that return a reference to the internal static variable,
int & wrapValue (int v0)
{
static int val;
return val = v0;
}
int main ()
{
// how to permit this ...
int & v0 { wrapValue(0) };
// ... but forbid this ...
int v1 { wrapValue(1) };
int v2;
// ... and this ?
v2 = wrapValue(2);
}
there is a way to permit the initialization of v0 (and bound v0 to the static variable) and forbid the initialization of v1 and the assignment of v2 (without bounding v1 and v2 to the static variable) ?
And if it's impossible with the current C++ standard, as I fear, someone can suggest me an alternative way (but not too complex: I intend use it in a library that I want to maintain simple) to forbid an unbounded assignment ?
This solution is somewhat tricky but it works (I think) as you expect:
#include <iostream>
struct int_wrapper {
int value;
int_wrapper &operator=(int value) {
this->value = value;
return *this;
}
operator int&() {
return value;
}
operator int() {
return value;
}
};
int_wrapper& wrapValue (int v0) {
static int_wrapper val;
return val = v0;
}
int main () {
// how to permit this ...
int & v0 = wrapValue(0);
// ... but forbid this ...
//int v1 { wrapValue(1) }; // call ambigious
int v2;
(void)v0;
(void)v2;
// ... and this ?
//v2 = wrapValue(2); // call ambigious
}
[live demo]
As far as I know int is copyable, so people can copy if they like; you cannot prevent this. However, you could create a wrapper class that is non-copyable.
class NonCopyableInt
{
int val;
public:
NonCopyableInt(int val) : val(val) {}
NonCopyableInt(NonCopyableInt&) = delete;
int& value() { return val; }
// todo: add some nice operators and functions such as assignment from int
}
NonCopyableInt& wrapValue (int v0)
{
static NonCopyableInt val;
return val = v0;
}
However, people could always copy the return value from value() so you end up with the same problem. And it feels really clunky and meh.

C++: overloading subscript operator (different return typ)

I know that a return value is not enough to override a function (and I read different threads about it on stackoverflow), but is there a way to overload the subscript operator of a class, just to return the value (I can't changed to to return by reference-type of function)
It has to look like or at least work like:
What's the best approach to solve this problem (It has to be a operator)?
Update:
The Problem is, that's not allowed to just overload a member or operator just with the return type.
struct A {int a; int b; double c;};
struct B {int a; int b; array<double,2> c;};
class Whatever
{
public:
A operator [](unsigned int ) const; //it's just reading the element
B operator [](unsigned int ) const;
}
A operator [](unsigned int Input){
//...
}
B operator [](unsigned int Input){
//...
}
Assuming you know what kind of type you are going to access, you can return a proxy which converts to either type and, probably, does the access upon conversion. Since you want to access a const object that should be fairly straight forward. Things get a bit more messy when trying to do the same for an updating interface:
class Whatever;
class Proxy {
Whatever const* object;
int index;
public:
Proxy(Whatever const* object, int index)
: object(object)
, index(index) {
}
operator A() const { return this->object->asA(this->index); }
operator B() const { return this->object->asB(this->index); }
};
class Whatever {
// ...
public:
Proxy operator[](int index) { return Proxy(this, index); }
A asA(index) { ... }
B asB(index) { ... }
};
The main constraint is that you can't access members of A and B directly: you need to convert to an A or a B first. If the two types are actually known, you can created a Proxy which forwards the respective member function appropriately. Of course, if there are commonly named member functions or data members you'll need to explicitly convert first.

Whats the significance of return by reference?

In C++,
function() = 10;
works if the function returns a variable by reference.
What are the use cases of it?
The commonest case is to implement things like operator[].
struct A {
int data[10];
int & operator[]( int i ) {
return data[i];
}
};
Another is to return a big object from a class via an accesor function:
struct b {
SomeBigThing big;
const SomeBigThing & MyBig() const {
return big;
}
};
in order to avoid the copying overhead.
Consider the following code, MyFunction returns a pointer to an int, and you set a value to the int.
int *i;
i = MyFunction();
*i = 10;
Now shorten that to
*(MyFunction()) = 10;
It does exactly the same thing as the first code block.
You can look at a reference as just a pointer that's always dereferenced. So if my function returned a reference - not a pointer - to an int the frist code block would become
int &i;
i = MyFunction();
i = 10;
and the second would become
MyFunction() = 10;
This is what i was looking for
Getters/setters for instance
class C
{
int some_param_;
public:
int& param() { return some_param_; }
int const& param() const { return some_param_; }
};
but here you should go with some_param being a public int. Containers provide functions that return by reference, eg. vector<T>::operator[] so that you can write v[k] = x.
A very normal use case is when you write an array like class. Here you want to overload the operator [] so as you can do a[0] = 10; In that case you would want the signature to be like int& operator[](int index);
In case you have a class that contains another structure, it can be useful to directly modify the contained structure:
struct S
{
int value;
};
class C
{
public:
S& ref() { return m_s; }
private:
S m_s;
};
Allows you to write something like:
void foo()
{
C c;
// Now you can do that:
c.ref().value = 1;
}
Note: in this example it might be more straightforward to directly make m_s public rather than returning a reference.
SO screwed up my answer
You don't even need to return a reference:
struct C { };
C f() {
return C();
}
int main() {
C a;
f() = a; // compiles fine
}
Because this behavior is quite surprising, you should normally return a const value or a const reference unless the user has a sensible intent to modify the result.
It can be usefull when implementing accessors
class Matrix
{
public:
//I skip constructor, destructor etc
int & operator ()(int row, int col)
{
return m_arr[row + col * size];
}
private:
int size;
int * m_arr;
}
Matrix m(10);
m(1,0) = 10; //assign a value to row 1, col 0
Another classic case:
class Foo {
Foo();
public:
static Foo& getSingleton();
};
std::vector has operator[] which would not allow vec[n] = m otherwise.
You can also achieve method chaining (if you so desire) using return by reference.
class A
{
public:
A& method1()
{
//do something
return *this; //return ref to the current object
}
A& method2(int i);
A& method3(float f); //other bodies omitted for brevity
};
int main()
{
A aObj;
aObj.method1().method2(5).method3(0.75);
//or use it like this, if you prefer
aObj.method1()
.method2(5)
.method3(0.75);
}
The named parameter idiom is a another use case. Consider
class Foo
{
public:
Foo(
int lions,
float tigers,
double bears,
std::string zookeeper
);
};
users of this class need to remember the position of each parameter
Foo foo( 1, 2.0, 5, "Fred" );
which can be non-obvious without looking at the header. Compared to a creator class like so
class CreateFoo
{
friend class Foo;
public:
CreateFoo();
CreateFoo& lions(int lions) {
_lions = lions;
return *this;
}
CreateFoo& tigers(float tigers) {
_tigers = tigers;
return *this;
}
CreateFoo& bears(double bears) {
_bears = bears;
return *this;
}
CreateFoo& zookeeper(const std::string& zookeeper) {
_zookeeper = zookeeper;
return *this;
}
private:
int _lions;
float _tigers;
double _bears;
std::string _zookeeper;
};
which can then be used by clients like so
Foo foo = CreateFoo().
lions(1).
tigers(2.0).
zookeeper("Fred").
bears(5)
;
assuming Foo has a constructor taking a const CreateFoo&.