Writing to static member variable

This has to be a really simple issue, but for the life of me, I can't see what I'm doing wrong.

The problem arose in a bigger application, but I have extracted the nub of the problem & put it into a trivial sketch to expose it.

I have created a new class definition & all I've put into it is a static member variable. That compiled just fine. I can quite happily put a statement in the setup or loop procedure that reads from that variable, and that was fine. But when I put in a statement that writes to it, compiler tells me "Error compiling for board Arduino Pro or Pro Mini".

This is the diagnostic report:-

C:\Users\greg\AppData\Local\Temp\ccyM7umn.ltrans0.ltrans.o: In function `main':

ccyM7umn.ltrans0.o:(.text.startup+0x96): undefined reference to `Clock::m_snCycles'

ccyM7umn.ltrans0.o:(.text.startup+0x9a): undefined reference to `Clock::m_snCycles'

collect2.exe: error: ld returned 1 exit status

exit status 1
Error compiling for board Arduino Pro or Pro Mini.

Here's the whole of my sketch:-

class Clock{ public: static int m_snCycles;};
void setup() {}
void loop() { Clock::m_snCycles = 1;}

This seems like the sort of thing that is obvious to most, but I just can't see it.

You have defined the variable but you ever initiated it.

class Clock{
  public:
    static int m_snCycles;
};
int Clock::m_snCycles = 0;

void setup() {
  
}
void loop() {
  Clock::m_snCycles = 1;
}

And please use code tags next time and watch you indentation. While your code is very compact now, it's also very hard to read. NOTHING follows a {, } or ; (except in a loop declaration).

And why do you want this? Normally it's good practice to have get'ers and set'ers to ensure encapsulation.

Thanks septillion. The answer is I need to initialise the static member variable.

Yes, we could argue the merits of get'ers & set'ers, but that is a new issue that only increases the size of example without dealing with the problem. A get'er would only need to read the variable, and the compiler doesn't object to that. A set'er only moves the same problem to the set'er procedure itself. I would still need to initialise the static member variable.

I chose to collapse the extraneous code that is not pertinent to the problem. I thought I was helping. Again, this is raising another issue, and diverting attention away from the problem at hand.

"Use fricking code tags!!!" - OK - If there's an established protocol, I want to fit in with that, but I don't know the rules of engagement yet. Unfortunately, to do that, I've got to learn more than I already know.

Yes, I am new to this, and I really appreciate your prompt response. I would not have put the question out without having first put in quite a lot of my time without solving the problem. It's not obvious to me that assigning a value to the variable in the setup procedure is anything other than initialising it. But that didn't work. It's not obvious to me that I could read the member variable without pre-initialising it, but I couldn't write to it.

I thought that by declaring a static member variable in the class was reserving the data storage space for it. The fact that I could read it appears to confirm that. Therefore, I thought I would be able to preset it by writing to it in the setup procedure. Clearly, I was wrong, but I don't feel that I'm an idiot for having thought that.

"Go and pay someone to do the job ..." - I find that a bit tough. As a newbie, it doesn't make me feel very welcome.

I don't know the rules of engagement yet.

Can I suggest that you read this before posting a programming question which is one of the stickies at the top of the forum page.

rtdgreg:
I chose to collapse the extraneous code that is not pertinent to the problem. I thought I was helping. Again, this is raising another issue, and diverting attention away from the problem at hand.

That's fine. You made compileable code with the same problem so no problem there.

rtdgreg:
"Use fricking code tags!!!" - OK - If there's an established protocol, I want to fit in with that, but I don't know the rules of engagement yet. Unfortunately, to do that, I've got to learn more than I already know.

That's why there is a nice sticky at the top of each board called How to use this forum :slight_smile:

rtdgreg:
It's not obvious to me that assigning a value to the variable in the setup procedure is anything other than initializing it.

Look closely, I'm not :wink:

rtdgreg:
It's not obvious to me that I could read the member variable without pre-initialising it, but I couldn't write to it.

But you can't...

class Clock{
  public:
    static int m_snCycles;
};

void setup() {
  
}
void loop() {
  Serial.print(Clock::m_snCycles);
}

gives you the same error....

rtdgreg:
"Go and pay someone to do the job ..." - I find that a bit tough. As a newbie, it doesn't make me feel very welcome.

I don't :slight_smile: We are all just volunteers around here. And if you somehow have the need to get something fast then you should also have the resources to accomplish that. And if it's not your knowledge then yeah, don't expect to get it for free. Do you know the saying "Cheap, fast, good. You can only pick two..."

"Use fricking code tags!!!" "Go and pay someone to do the job ..."

Those last bits are part of Septillion's forum signature, and weren't directed at you in particular.
You'd think the forums were full of requests from people demanding to have their homework done for them after they skipped classes, didn't do any reading, AND waited till the last minute, AND not forming either their thoughts, their post, or their sample code into anything ... comprehensible.

I particularly liked the "compression" of your example. But only because your question was of a depth and specificity that it was clear that the punctuation was correct; otherwise I'd hate to analyze ";};"...

Let me see if I understand this from a C++ perspective. (I keep feeling like I understand more about C++, and then some weird example turns up that needs me to re-examine my assumptions!)

  • A static class variable is one that occurs once per class, rather than once per instance of a class object?
  • But the class definition still only defines the class without defining storage/etc.
  • So you need to explicitly reference the class and initialize the static variable before it can set the static variable?

Would it have worked if the variable had been initialized within the class definition? (The way static function variables are usually handled?)
Would it have worked if an object of the class had been created before trying to write it?
Why did the read succeed?
What does this statement actually DO?

int Clock::m_snCycles = 0;

It doesn't instantiate an object, right? But it DOES cause creation, rather than just definition. If the class had contained other static variables, would they have become accessible as well, or would they all need to be individually initialized?

"But you can't..."

The following compiled for me without error:-

class Clock{ public: static int m_snCycles; };

void setup() {
}

void loop() {
int nTemp;
nTemp = Clock::m_snCycles;
//Clock::m_snCycles = 1;
}

It's [], not <>.

There is an icon - top row, far left - that looks like </> that generates the proper tags. Yeah, it doesn't make sense that the icon doesn't match what the result looks like, but we've complained for three years, and that's the best the web developer can come up with.

Thanks Paul,

I will get the hang of it soon.

rtdgreg:
The following compiled for me without error:-

That's because you did not use the value for anything and the compiler optimized it away.
Add Serial.println(nTemp); and see what happens.

Interesting discussion westfw. I think I was misled a bit by the fact that reading the static member variable was fine.

It makes me wonder if this is platform specfic - or, rather, compiler specific.

Originally, I thought I had simply made a basic schoolboy error that was staring me in the face, but I wasn't seeing. Maybe I ought to go to Stroustrupp. Yes, just this minute I have gone to my bookshelf & it is there, gathering dust. I hadn't lent it to someone else & not got it back. Mine is 1991 - second edition. I wonder, did I ever have an earlier copy? Don't know.

I suppose I am rusty. I have just tried adding the statement ::<;> without any assignment. That too is just fine.

You raised the point:-

What does this statement actually DO?

int Clock::m_snCycles = 0;

It doesn't instantiate an object, right? But it DOES cause creation, rather than just definition. If the class had contained other static variables, would they have become accessible as well, or would they all need to be individually initialized?

You didn't provide the answers, and I don't think I can, at the moment, but as I said, the preset isn't mandatory. I think it's a cusiosity that using the member on the left side of the assignment throws an error, but not on the right when there's no <int Clock::m_snCycles;> but doesn't when there is. I would put that down to an idiosyncrasy of the compiler. But is this an indicator that the storage is allocated when the class is defined? That could be so. It could well be the compiler is just checking symantics & throwing an error if it encounters an assignment of the member variable without the explicit reservation.

I am agreeing with your conclusion, I think. Defining a static member does create the machine storage. Instantiation creates the machine storage for the non static members. Going on ... I think it's just the compiler throwing an error because program language requirments have not been met, and, with this particular compiler, the error checking has been implemented on the "write" side, but not for "read". It could well be that other compilers are more rigorous (snagging read operations) or less rigorous (not snagging write operations).

Thanks for your input.

oqibidipo - yes, I agree with your conclusion.

oqibidipo nailed it :slight_smile:

A class is just a blueprint. It does not claim memory on it own, not even for the statics. It does nothing. It's just you describing some sort of new "super" variable you wish you could create. You have to define the memory for it if you want to use it.

And I'm not a super C++ guru but I'm pretty sure that's just C++, not the compiler.

rtdgreg:
I suppose I am rusty. I have just tried adding the statement ::<;> without any assignment. That too is just fine.

Yep, that just defines the variable without initializing it :slight_smile:

rtdgreg:
would they all need to be individually initialized?

You have to do that for all....

rtdgreg:
I think it's a cusiosity that using the member on the left side of the assignment throws an error, but not on the right when there's no <int Clock::m_snCycles;> but doesn't when there is.

You just can't use it, not matter where. But the compiler is smart. If you just assign it to a variable but you never use that variable it just doesn't create the variable. And if the variable doesn't excist it will not try to put Clock::m_snCycles into it thus not trowing an error.

rtdgreg:
But is this an indicator that the storage is allocated when the class is defined?

Only for that object, not the statics.

Yes. The following works, but commenting out line 2 throws an error.

class Clock{ public: static int m_snCycles;};
int Clock::m_snCycles;
void setup() {}
void loop() { Clock::m_snCycles = 1;}