4

I'm coming from a background in languages like Actionscript 3 where we have a special way of defining a member variable as both an instance and a method for setting/fetching the value of a protected or private member. Let me give an example:

Within a class, we can say something like this:

private var _myString:String;

public get myString():String 
{
    return _myString;
}

public set myString(newValue:String):void
{
    //Do some super secret member protection n' stuff
    _myString = newValue;
}

And then outside of that object I can do the following:

trace(myClass.myString); //Output whatever _myString is. (note the lack of (). It's being accessed like property not a method...

And even further, I could do something like delete the "public set myString" method, so if someone tried to do this with my class:

myClass.myString = "Something"; //Try to assign - again note the lack of ()

It would throw an error, letting the user know that the property is available as read-only.

Now since I'm using C++ and it's infinitely more awesome than Actionscript 3, I'm wondering how I can mimic this type of behavior. I don't want to use a bunch of dirty getVariable() and setVariable() methods. I was hoping through some operator overloading trickery I could make the exact same thing possible here. Note I am a noob, so please address me as such. :)

Update I guess the easiest way to explain this is, I'm trying to essentially have getters and setters but invoke them through assignment rather than with the parentheses ().

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • 1
    In C++, variable() and setVariable() calls are _not_ dirty. Depending upon your data types and internal class data they're actually safer than raw public variables. – Will Bickford Dec 10 '11 at 06:40

5 Answers5

8

Sorry, with C++, the syntactic sugar is not there, which means you have to implement the get/set methods yourself. So in your case you will have to implement a getVariable() method without it's equivalent Set method in order to make it read only.

And please don't resort to macro's to make it Look like you have some read only properties. That will just piss people off when they read your code 5 years from now.

C.J.
  • 15,637
  • 9
  • 61
  • 77
  • Marked your answer as correct. After all this time I realized this isn't part of the language and no one should try and force it to be. :) –  Nov 28 '13 at 07:40
  • 1
    But to be honest, I really miss the possibility to create read-only property in C++. Sometimes one just forgets that there was a reason not to change certain property. – Tomáš Zato Dec 08 '14 at 23:27
3

If I understand your question correctly, you want to create const public member variable in C++ terms.

class myClass
{
//...
public:
  const std::string myString;

  myClass(std::string s) : myString(s) {}
};

So now, myClass::myString is initialized when you declare the class object and it remains unmutable (read only) throughout its life time.

As side effect of this approach, now myClass objects cannot be default assignable. i.e.

myClass o1("s"), o2("t");
o1 = o2; // error
iammilind
  • 68,093
  • 33
  • 169
  • 336
  • s/default assignable/copy assignable/ – James McNellis Dec 10 '11 at 06:38
  • Unfortunately no that's not what I'm trying to accomplish. I'm trying to essentially have getters and setters but invoke them through assignment rather than with the parentheses (). That's the best way I can describe it. –  Dec 10 '11 at 06:38
  • @JamesMcNellis, I think it can be [default copy assignable](http://www.ideone.com/bQAw1). – iammilind Dec 10 '11 at 06:41
  • There is no such thing as "default assignable" or "default copy assignable." It's called copy assignability. – James McNellis Dec 10 '11 at 08:01
  • @JamesMcNellis: It makes sense to me: "default assignable" = "an assignment operator that is auto-generated by the compiler will not cause a compilation error". – j_random_hacker Dec 11 '11 at 05:54
2

Actually it is possible using templated primatives.

If you want a readonly variable but don't want the client to have to change the way they access it, try this templated class:

template<typename MemberOfWhichClass, typename primative>                                       
class ReadOnly {
    friend MemberOfWhichClass;
public:
    inline operator primative() const                 { return x; }

    template<typename number> inline bool   operator==(const number& y) const { return x == y; } 
    template<typename number> inline number operator+ (const number& y) const { return x + y; } 
    template<typename number> inline number operator- (const number& y) const { return x - y; } 
    template<typename number> inline number operator* (const number& y) const { return x * y; }  
    template<typename number> inline number operator/ (const number& y) const { return x / y; } 
    template<typename number> inline number operator<<(const number& y) const { return x <<y; }
    template<typename number> inline number operator>>(const number& y) const { return x >> y; }
    template<typename number> inline number operator^ (const number& y) const { return x ^ y; }
    template<typename number> inline number operator| (const number& y) const { return x | y; }
    template<typename number> inline number operator& (const number& y) const { return x & y; }
    template<typename number> inline number operator&&(const number& y) const { return x &&y; }
    template<typename number> inline number operator||(const number& y) const { return x ||y; }
    template<typename number> inline number operator~() const                 { return ~x; }

protected:
    template<typename number> inline number operator= (const number& y) { return x = y; }       
    template<typename number> inline number operator+=(const number& y) { return x += y; }      
    template<typename number> inline number operator-=(const number& y) { return x -= y; }      
    template<typename number> inline number operator*=(const number& y) { return x *= y; }      
    template<typename number> inline number operator/=(const number& y) { return x /= y; }      
    template<typename number> inline number operator&=(const number& y) { return x &= y; }
    template<typename number> inline number operator|=(const number& y) { return x |= y; }
    primative x;                                                                                
};      

Example Use:

class Foo {
public:
    ReadOnly<Foo, int> x;
};

Now you can access Foo.x, but you can't change Foo.x! Remember you'll need to add bitwise and unary operators as well! This is just an example to get you started

Jonathan
  • 6,741
  • 7
  • 52
  • 69
  • The solution is worse than the problem. I would never in a million years resort to such tricks. But it's clever, and I give you credit for that. – C.J. Dec 11 '17 at 17:37
2

It's not impossible to get the kind of behavior you want. But it's also not necessarily a good idea.

You can, in principle, do something like this:

template<typename T>
class AbstractGetter {
  T &refT;
public:
  operator const T() const { return refT; }
  AbstractGetter(T &refT_) : refT(refT_) { }
};

class Foo {
  int foo_private;
public:
  AbstractGetter<int> foo;

  Foo() : foo(foo_private) { }
};

HOWEVER: AbstractGetter here costs you sizeof(int&) in additional memory. And it's a rather ugly hack, too - if you start needing to represent anything more complex than just a reference (maybe your getter needs to have some logic?) either the syntax or memory overhead gets much worse.

As such, it's normal C++ style to use getter functions, rather than hacks like this.

bdonlan
  • 224,562
  • 31
  • 268
  • 324
  • 1
    What about adding a private setter to `AbstractGetter`, having it take `Foo` as a second template parameter and befriend `Foo`, and then having it store the `T` directly? – James McNellis Dec 10 '11 at 06:36
  • I agree. Even I can't understand this stuff... :) – C.J. Dec 10 '11 at 06:39
  • @JamesMcNellis, okay, I admit that would be better, but it wouldn't let you put any intelligence into the getter :) – bdonlan Dec 10 '11 at 06:41
  • 1
    I've found this answer: http://stackoverflow.com/questions/760777/c-getters-setters-coding-style. And the user "j_random_hacker" seems to have answered pretty much the same thing I'm just not fully understanding it. lol dunno if that helps to clarify anything. –  Dec 10 '11 at 06:44
  • @JamesMcNellis, actually, looks like template parameters can't be friends. :) – bdonlan Dec 10 '11 at 06:45
  • I think you can. Visual C++ and EDG both allow it. – James McNellis Dec 10 '11 at 08:07
  • 1
    I'm not saying I'd recommend doing this, though: I detest sneaky implicit conversions. – James McNellis Dec 10 '11 at 08:10
  • @JamesMcNellis, didn't seem to work on gcc, and googling around a bit suggested it's disallowed. VC++ may allow it as an extension – bdonlan Dec 10 '11 at 08:17
  • `friend class T` is ill-formed, but `friend T` is well-formed (explicitly in C++11, and implicitly, I believe, in C++03). – James McNellis Dec 10 '11 at 08:23
  • Hey guys thanks for all of your research and input, I've decided after considering the full scope of the problem to stop trying to be cool and just use normal getters and setters, as C Johnson has suggested. :( I may return to attempt this again someday when my understanding is a little broader. I can see from some research that QT has this type of functionality as well as other things I'm interested in. I just think for the scope of the project, and in the interest of just getting it done I'll go the easy, standard route. Thanks again. –  Dec 10 '11 at 16:15
0

C++ is flexible so you can do so much nasty things. it is possible but with bit more typing.

Here is a wrapper template.

// element wrapper for creating a getter and setter.
template <typename Base, typename T>
class getterSetter{
  T value;
  // below members required for setting up setter logic in owner class.
  Base *parent;            //owner object pointer
  bool (Base::*setter)(T&); //setter callback return true to set, false to not set.

public:
  getterSetter(T& v, Base *p, bool (Base::*set)(T&))
    : value(v),
      parent(p),
      setter(set)
  {} // read-write constructor.                                                                                                             
  getterSetter(T& v): value(v),parent(NULL), setter(NULL) {} // read only constructor.                                                      

  // setter implemented via operator overloading of = operator.
  const getterSetter& operator=(const T& v) {
    if (this->parent && this->setter && (parent->*(this->setter))(v)) {
      value = v;
    }
    else {
      // throw an exception here.                                                                                                           
    }
    return *this;
  }

  // cast sometimes helps for a getter.
  operator T() const{
    return value;
  }
};

The implementation be like

class MyClass {
public:
  getterSetter<MyClass, std::string> myString;
  MyClass(std::string v): myString(v, this, &test::setX){}
  // MyClass(std::string v): myString(v) {} ;//for readonly myString.
  MyClass(MyClass&) = delete;

  bool setX(std::string& v) {
    // your setter logic here.                                                                                                                     
    return true;
  }
};

By this way this can be used as setter and getter without paranthesis.

declare MyClass x("hello");

then x.myString = "new value"; // this will call setter implementation in MyClass

assignment like

std::string newString;
newString = x.myString;

will work.

Although this can be done, it is not a good thing to do in c++. it uses two additional pointer memory for each property which is bad. also need to write more operator overloading to work with STL and external code. There may be better way.