From: Martin T. on
Balog Pal wrote:
> "Martin T." <0xCDCDCDCD(a)gmx.at>
>>> Now, what is the percentage of classes/structs in a project that has a
>>> meaningful op==?
>>> And then, where it is based on full memberwise comparition?
>>>
>>> Guess if you find the special case, it will not change members all that
>>> often...
>> The use case for these structs is representing configuration data. As
>> such there are quite a few of them and I *do* expect them to change
>> quite frequently.
>
> Sure, I use configs, and indeed members come and go. I still can't recall
a
> case to want op == on the whole config. I read it at some place,
possibly
> write it out as a bulk, and use individual members... but how comparision
> comes in?
>
>> For these items memberwise equality is exactly
>> appropriate most of the time.
>
> It could 'work' indeed but for what situation?
> (btw I'd guess Boost has tools for such beasts -- tuple, any, ... if plain

> old struct is not enough)
>
>> Someone will forget to add to the operator== *especially* if it's only
>> used in the unit tests.
>
> Well, if it only used in UT, it is a good indication of unwanted ballast
> that has nothing to do in the real interface. The test should not be that

> intrusive. And if, for implementation of the harness it needs compare
that
> way -- I really would go with the earlier suggest: code generator.
>
>> The workaround I currently use to safeguard against this is:
>> 1.) operator== is defined in the header
>> 2.) Manually added static assert on the size of the struct
>>
>> inline bool operator==(Cfgdata const& lhs, Cfgdata const& rhs)
>> {
>> #ifdef NDEBUG
>> BOOST_STATIC_ASSERT(sizeof(Cfgdata) == 56); // Check size, so we don't
>> forget to add new members!
>> #endif
>> // ...
>
> Interesting idea really. My first line of thought would be 'scrap it'.
>
> OTOH I do have situations with similar maintain cases -- the 'offending'
> functions are mostly I/O routines (like stream op << >> ), and when adding
a
>
> member I follow the ritual to pick another member and look visit all
> instances...
>

I follow the same ritual.
However, case is just that there are other maintainers, me I've been
guilty of messing this up in the past, so every help I can give us in
not making stupid mistakes is better than weird bugs later.

cheers,
Martin

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Seungbeom Kim on
Balog Pal wrote:
>
> This "problem" is a top gotcha for ~ 2 decades. And has an extremely easy
> solution: do NOT write copyctor, op=. I mean it. Not in user code!
>
> [...]
>
> My experience is to just have two basic cases in user code: forbid cctor and
> op= or leave the auto-created. Exceptions are in the 'writing library'
> land, where it is increasingly hard to find a case of a new resource or
> collection not already covered.

Suppose you want to do something *in addition to* the default behaviour,
such as logging:

class Big
{
// lots of members
public:
Big(const Big&);
};

Big::Big(const Big& other)
: first(other.first)
, second(other.second)
, third(other.third)
// and so on
{
std::clog << "Big(" << this << ")::Big(const Big& = @"
<< &other << "):\n";
std::clog << "\tfirst=" << first << ", second=" << second
<< ", third=" << third << std::endl;
}

How would you solve this problem?

I have had more than a case where I had to introduce a user-defined
copy constructor and enumerate all members just to do such a logging.

--
Seungbeom Kim

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Balog Pal on
"Seungbeom Kim" <musiphil(a)bawi.org>
> Balog Pal wrote:
>>
>> This "problem" is a top gotcha for ~ 2 decades. And has an extremely
>> easy solution: do NOT write copyctor, op=. I mean it. Not in user code!
>>
>> [...]
>>
>> My experience is to just have two basic cases in user code: forbid cctor
>> and
>> op= or leave the auto-created. Exceptions are in the 'writing library'
>> land, where it is increasingly hard to find a case of a new resource or
>> collection not already covered.
>
> Suppose you want to do something *in addition to* the default behaviour,
> such as logging:

Heh, supposing is a good thing -- in moderation. But we;d better stick to
reality.
Copy ctor and op= anre not just "usual functions", they are special ones
with predetermined semantics. With the purpose to create a copy. So you
better not want to do anything beyond that in those finctions. Especially
as the compiler has license to omit them in certain situations, so the
addition may just be skipped.

What is the problem with putting the extra stuff in a properly named
function fuch as LoggedCopy() ;-)

> class Big
> {
> // lots of members
> public:
> Big(const Big&);
> };
>
> Big::Big(const Big& other)
> : first(other.first)
> , second(other.second)
> , third(other.third)
> // and so on
> {
> std::clog << "Big(" << this << ")::Big(const Big& = @"
> << &other << "):\n";
> std::clog << "\tfirst=" << first << ", second=" << second
> << ", third=" << third << std::endl;
> }

> How would you solve this problem?

By elimination.

(If you tell me some realistic example, I may give actual hints...
'general' problem solving is not the way of programming. )

Really, I used traces time-to-time, but not in said functions -- a LOG has
its use, but not on this technical level. {Unless, maybe if you have an
utter mess -- but it is likely an outcome of such ideas. }

> I have had more than a case where I had to introduce a user-defined
> copy constructor and enumerate all members just to do such a logging.

Okay, then you can tell the purpose and usefulness of why it was good, and
what that log was used for. I am really curious.


And well, if there is an absolute need, then you go and maintain the list of
members. No hunters - no hens, we don't have the perfect world. But a
language needs no tuning or support for some extreme cases.

Btw if it's enough to drop a line in the log, it can be done using a
stateless member.
If just for instrumentation, I (likely) could create one that could call the
enclosing class's Dump function too, so if placed properly at the end of the
host, can do what you did above. (just from top of my head)

class Big;

template <class T>
struct LogTweak {
static void Log(const T * host);
const T * GetHostAddr() const;
LogTweak() { Log(GetHostAddr());}
LogTweak(const LogTweak &) { Log(GetHostAddr());}
};

class Big
{
public:
string State2String() { ... }

private:
// must be last member
LogTweak<Big> last;
};

Log() calls the host's renderer, no problem here.

GetHostAddr() is the tricky part, but with some ugly trick and casts the
address can be figured, having Big b; ptrdiff (char *) &b.last - (char *)
&b; then using that offset substracted from this. the offsetof macro is
POD-only, but i'm sure there are general solutions providin it as
compile-time constant (and even the stock one shall work in practice for
regular members).





--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: tohava on
On Sep 10, 11:01 pm, Seungbeom Kim <musip...(a)bawi.org> wrote:
>
> Suppose you want to do something *in addition to* the default behaviour,
> such as logging:
>
> class Big
> {
> // lots of members
> public:
> Big(const Big&);
>
> };
>
> Big::Big(const Big& other)
> : first(other.first)
> , second(other.second)
> , third(other.third)
> // and so on
> {
> std::clog << "Big(" << this << ")::Big(const Big& = @"
> << &other << "):\n";
> std::clog << "\tfirst=" << first << ", second=" << second
> << ", third=" << third << std::endl;
>
> }
>
> How would you solve this problem?

class BigData {
// here are only data members...
}

class Big : public BigData {
Big(const Big& other) : BigData(other)
{
// logging stuff
}
}
>
> I have had more than a case where I had to introduce a user-defined
> copy constructor and enumerate all members just to do such a logging.

{ edits: quoted sig & banner removed. don't quote extraneous material. tia., -mod }


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Seungbeom Kim on
Balog Pal wrote:
> "Seungbeom Kim" <musiphil(a)bawi.org>
>> Suppose you want to do something *in addition to* the default behaviour,
>> such as logging:
>
> Heh, supposing is a good thing -- in moderation. But we;d better stick to
> reality.

I didn't make that up; that was real.

> Copy ctor and op= anre not just "usual functions", they are special ones
> with predetermined semantics. With the purpose to create a copy. So you
> better not want to do anything beyond that in those finctions. Especially
> as the compiler has license to omit them in certain situations, so the
> addition may just be skipped.

I know that, and I use them for what they're precisely meant for.
All I wanted to do was to log the event that happened. Are you
suggesting that such a logging should happen in another function?
How should that function be called during a copy construction or
an assignment?

>
> What is the problem with putting the extra stuff in a properly named
> function fuch as LoggedCopy() ;-)

That doesn't serve my purpose of logging every copy construction event.
I cannot control how the clients make a copy and change it, and I just
have to assume that the actual copy constructor is called everywhere.

>
>> class Big
>> {
>> // lots of members
>> public:
>> Big(const Big&);
>> };
>>
>> Big::Big(const Big& other)
>> : first(other.first)
>> , second(other.second)
>> , third(other.third)
>> // and so on
>> {
>> std::clog << "Big(" << this << ")::Big(const Big& = @"
>> << &other << "):\n";
>> std::clog << "\tfirst=" << first << ", second=" << second
>> << ", third=" << third << std::endl;
>> }
>
>> How would you solve this problem?
>
> By elimination.
>
> (If you tell me some realistic example, I may give actual hints...
> 'general' problem solving is not the way of programming. )

Please do not assume that I'm challenging you with a fictitious example;
wanting to log some event is a genuine need, and it helps little to say
"just don't do that" (assuming that's what you meant by "elimination").

>
> Really, I used traces time-to-time, but not in said functions -- a LOG has
> its use, but not on this technical level. {Unless, maybe if you have an
> utter mess -- but it is likely an outcome of such ideas. }
>
>> I have had more than a case where I had to introduce a user-defined
>> copy constructor and enumerate all members just to do such a logging.
>
> Okay, then you can tell the purpose and usefulness of why it was good, and
> what that log was used for. I am really curious.

Okay, to tell you the exact truth, I wanted to map every object with
a unique ID (other than its address) and log that (along with some
other member data) to facilitate debugging in later stages. AFAICS,
nowhere else than the constructors are the right places to do it.

>
> And well, if there is an absolute need, then you go and maintain the list
of
> members. No hunters - no hens, we don't have the perfect world. But a
> language needs no tuning or support for some extreme cases.

It's just that it's hard for me to accept that it's such an extreme
case and that it's supposed to be so hard to solve. It is hard indeed
with about a few dozen member variables (I wish it had fewer, but I
didn't write it, and I have to maintain it!), and the possibility of
a mistake of omitting some members is much more serious a burden than
the labour of enumerating all members.

>
> Btw if it's enough to drop a line in the log, it can be done using a
> stateless member.
> If just for instrumentation, I (likely) could create one that could call
the
> enclosing class's Dump function too, so if placed properly at the end of
the
> host, can do what you did above. (just from top of my head)
>
> class Big;
>
> template <class T>
> struct LogTweak {
> static void Log(const T * host);
> const T * GetHostAddr() const;
> LogTweak() { Log(GetHostAddr());}
> LogTweak(const LogTweak &) { Log(GetHostAddr());}
> };
>
> class Big
> {
> public:
> string State2String() { ... }
>
> private:
> // must be last member
> LogTweak<Big> last;
> };
>
> Log() calls the host's renderer, no problem here.
>
> GetHostAddr() is the tricky part, but with some ugly trick and casts the
> address can be figured, having Big b; ptrdiff (char *) &b.last - (char
*)
> &b; then using that offset substracted from this. the offsetof macro is
> POD-only, but i'm sure there are general solutions providin it as
> compile-time constant (and even the stock one shall work in practice for
> regular members).

It's too ugly a trick as you admit, and I doubt if there are general
solutions, for: if there were one then why couldn't offsetof do it?
I guess there's something inherently unreliable that prevents offsetof
from being applicable entirely generally.

--
Seungbeom Kim

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]