If it's const, it can't be changed by anyone. If it's non-const, then
it can be modified by anyone who can access it. If it's public, it
can be accessed by anyone.
Thus, non-const public data is inherently writeable by anyone.
You may use a trick, however, that does involve private data. Suppose
your public data is called "pubdata", you can do this:
class Foo
{
public:
Foo();
int const & pubdata;
void set_data(int x);
private:
int data;
};
Foo::Foo() : pubdata(data), data(0)
{ }
void Foo::set_data(int x) { data = x; }
int main()
{
Foo f;
int val = f.pubdata; // OK
f.set_data(999); // OK
f.pubdata = 123; // ERROR
}
Member functions can modify data, and it's private, so other code
cannot access it. However, publically, you have pubdata, which is a
reference to a const int, and it refers to data. Thus, when accessing
the data through pubdata, it's in a const context, but when accessing
it through data, it's private and writeable--which matches your
requirements: only writeable by members, readable by everyone publically.
Will this work for you?
--
Chris (TeamB);
> I have a class member variable. I like it can be read directly but can be
writen only by call member function.
You can use Borland's proprietary '__property' keyword, ie:
class MyClass
{
private:
int val;
void __fastcall SetValue(int newval)
{
//...
val = newvalue;
//...
}
public:
__property int Value = {read=val, write=SetValue};
};
> I don't want to make it as a private variable since there are
> too many changes to current codes if do so.
Making it private is the only way to prevent it from being directly
writable. Using the __property keyword, you can name the property anything
you wish, so make the actual variable private so it is not directly
accessible, then rename it for class's internal use, and then finally name
the property the same name as the old variable name.
Gambit
Hi Gambit
Thanks. It should work for me. My variable type is char array.
I got compile error: "array property missing ]"
Here is the code. what's wrong?
public:
__property char name[32] = {read=devName, write=SetName};
virtual void __fastcall SetName(char*newName)
{strcpy(devName,newName);};
private:
char devName[32];
> My variable type is char array.
> I got compile error: "array property missing ]"
> Here is the code. what's wrong?
> public:
> __property char name[32] = {read=devName, write=SetName};
name[32] would be the 33th element of the array.
This construction is not possible. Besides you use
strcpy() instead of strncpy ( , 32 - 1) and placeing
a terminating zero.
All in all better use AnsiString:
private:
AnsiString devName;
void SetName(AnsiString newName)
{
devName = newName;
}
public:
__property AnsiString Name = {read=devName, write=SetName } ;
Hans,
> Thanks. It should work for me. My variable type is char array.
I wish you had said that earlier. You cannot use char[] with properties.
You will have to use char* instead, ie:
public:
__property char* name = {read = devName, write = SetName};
protected:
virtual void __fastcall SetName(char* newName)
{
strncpy(devName, newName, 32);
}
private:
char devName[32];
Gambit
> public:
> __property char* name = {read = devName, write = SetName};
In bcb5 this does not compile.
//[C++ Error] Unit1.cpp(75): E2347 Parameter mismatch
//in read access specifier of property name
To use a real char pointer:
__property char* name = {read = devNamePtr, write = SetName};
and
private:
char devName[32];
char *devNamePtr;
But then you need a constructor that innitialises
devNamePtr with the address of the first byte of devName[];
Aclass(){devNamePtr = devName;}
> protected:
> virtual void __fastcall SetName(char* newName)
> {
> strncpy(devName, newName, 32);
add here a terminating zero:
devName [32-1] = 0;
For a test: mit the statement and see what it does in:
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Aclass aclass;
aclass.name = "aaaaaaaaab ddddddddddd ggggggggggg hhhhhhhh iiiiii";
ShowMessage ( aclass.name );
}
Hans.
> In bcb5 this does not compile.
>
> //[C++ Error] Unit1.cpp(75): E2347 Parameter mismatch
> //in read access specifier of property name
You will just have to use a getter method then:
public:
__property char* name = {read = GetName, write = SetName};
protected:
char* __fastcall GetName()
{
return devName;
}
virtual void __fastcall SetName(char* newName)
{
strncpy(devName, newName, 32);
}
private:
char devName[32];
> add here a terminating zero:
>
> devName [32-1] = 0;
strncpy() already null-terminates the buffer, as long as the buffer has
enough space available. If the source string is >= 32 characters, the null
terminator will not be written, unless 31 is specified to strncpy() instead
of 32.
Gambit
> You will just have to use a getter method then:
Indeed that is good solution then.
> strncpy() already null-terminates the buffer, as long as the buffer has
> enough space available. If the source string is >= 32 characters, the null
> terminator will not be written, unless 31 is specified to strncpy() instead
> of 32.
No. strncpy does not add a zero. And the buffer is filled at random
at first. strncpy just copies the amount of bytes specified.
If one of them is a zero strncpy copies that one also and then stops.
So it is not guaranteed that there is a terminating
zero. For that
devName [32-1] = 0;
is always needed.
Hans.
> No. strncpy does not add a zero.
Yes, it does:
"strncpy copies up to maxlen characters from src into dest, truncating
***or null-padding dest.**"
Notice the last part. Any remaining space in the buffer after the copy is
finished gets initialized with zeros. Here is a test to prove it:
char buffer[32];
memset(buffer, ' ', 32);
strncpy(buffer, "test", 32);
Characters 4-31 have been changed from ' ' to '\0'. Please refer back to my
earlier comment:
"strncpy() already null-terminates the buffer, ***as long as the buffer
has enough space available.***"
> And the buffer is filled at random at first.
Only if you do not initialize it yourself before calling strncpy(). You
should always initialize your buffers before using them.
> strncpy just copies the amount of bytes specified.
It does more than that.
> So it is not guaranteed that there is a terminating zero.
If the buffer is large enough to contain the full source string and a
terminator, then yes, the terminator *is* guaranteed, as long as you specify
that much space is available when passing the buffer's size to strncpy().
> devName [32-1] = 0;
>
> is always needed.
It is not always needed. Only when the length of the source string is >=
the size of the buffer. You can account for that, though:
char devName[33] = {0};
strncpy(devName, "some string", 32);
Gambit
> Yes, it does:
>
> "strncpy copies up to maxlen characters from src into dest, truncating
> ***or null-padding dest.**"
Indeed it does.
> "strncpy() already null-terminates the buffer, ***as long as the buffer
> has enough space available.***"
For me that is the null-terminator of src.
>>And the buffer is filled at random at first.
> Only if you do not initialize it yourself before calling strncpy(). You
> should always initialize your buffers before using them.
And because you did not in your post for didan I reacted.
But then initialize *before* is not needed. In fact I never do that.
Just as good is placing a terminating zero as I proposed.
Hans.
> Remy Lebeau (TeamB) wrote:
>
>> You will just have to use a getter method then:
>
> Indeed that is good solution then.
>
>> strncpy() already null-terminates the buffer, as long as the buffer has
>> enough space available. If the source string is >= 32 characters, the null
>> terminator will not be written, unless 31 is specified to strncpy() instead
>> of 32.
>
> No. strncpy does not add a zero. And the buffer is filled at random
strncpy will null-terminate a buffer whenver it's called UNLESS you
ask it to write more data than the buffer can hold.
> So it is not guaranteed that there is a terminating
> zero. For that
>
> devName [32-1] = 0;
>
> is always needed.
I agree you need to write code like that (well, '\0' is one byte,
while 0 is an int requiring a conversion...), but in the case that the
buffer is large enough, this is just precaution. In the case where
the buffer is not big enough, you need to do this. Since it's hard to
know in advance if the buffer will be big enough or not, it's an
unwritten rule to always write a '\0' to that last byte. The overhead
is minimal, and sometimes it prevents bugs.
--
Chris (TeamB);
> virtual void __fastcall SetName(char* newName)
> {
> strncpy(devName, newName, 32);
> }
> strncpy() already null-terminates the buffer, as long as the buffer has
> enough space available. If the source string is >= 32 characters, the null
> terminator will not be written, unless 31 is specified to strncpy() instead
> of 32.
Which is why I like to write code like this:
strncpy(devName, newName, SIZE);
devName[SIZE-1] = '\0';
Just in case the trailing null is missing...
Though the C99 spec always null terminates the function results,
regardless of truncation. So worrying about this will likely become
archaic eventually, when/if C++ adopts C99 features.
--
Chris (TeamB);
Great Minds!
One function in my private libs that I use all the time is SafeStrncpy.
Three guesses what it does <g>
. Ed
> Chris Uzdavinis wrote in message
> news:j5is0qn...@explicit.atdesk.com...
> .... (well, '\0' is one byte,
> while 0 is an int requiring a conversion...),
This remark will change my future code.
Thanks.
Hans.
> One function in my private libs that I use all the time is SafeStrncpy.
> Three guesses what it does <g>
void SafeStrncpy ( char *ptr )
{
char *SafeStrn = "I am a safe string";
strcpy ( ptr, SafeStrn );
}
char buffer[10];
SafeStrncpy ( buffer );
This is only one guess ;-).
Hans.
char *SafeStrncpy(char *dest, char *src, int maxlen)
{
if (dest && src)
{
strncpy(dest, src);
dest[maxlen - 1] = '\0';
}
else
dest = NULL;
return dest;
}
Can't give exact source because I've not yet recovered the data from the
dead disk in my dead machine.
. Ed
> Hans Galema wrote in message
> news:42a5...@newsgroups.borland.com...
You are serious.
I was not.
>> char buffer[10];
>>
>> SafeStrncpy ( buffer );
That was an intended bufferoverflow.
Found it quite funny. LOL.
Sorry that you missed it.
Hans.
> More like this:
>
> char *SafeStrncpy(char *dest, char *src, int maxlen)
> {
> if (dest && src)
> {
> strncpy(dest, src);
> dest[maxlen - 1] = '\0';
> }
> else
> dest = NULL;
>
> return dest;
> }
This looks about like what I'd have guessed, except that maxlen would
be used in the call to strncpy. :)
I'm not sure I'd have dealt with null pointers, though, but with the
name "SafeXXXXXX" it probably should.
--
Chris (TeamB);
I am sure it is in the actual function - what you saw was quickly written
from foggy memory :-(
. Ed
> Chris Uzdavinis wrote in message
> news:j5fyvtn...@explicit.atdesk.com...
>> This looks about like what I'd have guessed, except
>> that maxlen would be used in the call to strncpy. :)
>
> I am sure it is in the actual function - what you saw was quickly written
> from foggy memory :-(
I know. I was just having fun.
--
Chris (TeamB);
LOL. I have tons of source on an old 286 that I keep around
because it represents lots of work but in reality is worthless
to me and most anyone else because it's all TurboC, Pascal, BAL
and COBAL. Still ... I can't bring myself to part with it.
~ JD
I have a box filled with 5 1/4" and 3 1/2" floppies containing the messages
from Borland's Compuserve C/C++ and Pascal forums as well as thousands of
*.pas, *.c, *.cpp and *.asm files. I've no idea what or even if I will do
anything with them. (FYI: they begin with ones created with a 4.77 MHz 8088
and some of the 5 1/4" ones are 320K!)
. Ed
> JD wrote in message
> news:42a6b093$1...@newsgroups.borland.com...