From: Seungbeom Kim on
tohava wrote:
> 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:
>>
>> [example snipped]
>>
>> 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
> }
> }

It requires a complete rewriting of the members, which is too intrusive.
Instead of mentioning the members in the copy constructor, you have to
move as many members and update all their uses, so how much does this
save? Moreover, you cannot enforce that all the data members belong to
BigData and not Big; someone could add a data member to Big and that
could go unnoticed, couldn't it?

--
Seungbeom Kim

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

From: Ulrich Eckhardt on
Seungbeom Kim wrote:
> tohava wrote:
>> 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:
>>>
>>> [example snipped]
>>>
>>> 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
>> }
>> }
>
> It requires a complete rewriting of the members, which is too intrusive.

Um, no?

Big b;
b.foo += "bar";

This stays exactly the same as before. Even if you don't use public members,
this remains the same for any function with access.

> Instead of mentioning the members in the copy constructor, you have to
> move as many members and update all their uses, so how much does this
> save? Moreover, you cannot enforce that all the data members belong to
> BigData and not Big; someone could add a data member to Big and that
> could go unnoticed, couldn't it?

Actually I find this one of the most appealing approaches. However:
1. Don't make BigData a baseclass, let alone a public one.
2. Don't call it BigData but Big::Data, i.e. use a nested type definition.
3. Make it private.

Accidentally adding something to Big instead of Big::Data is a small risk,
because both are declared very close to each other and they are obviously
strongly tied.

Note of course that my initial comment about the non-changing members
doesn't hold with the suggested changes. Maybe using a private data-class
as base would work, but then of course you can't nest it. Anyhow, if you
happen to use 'm_' as prefix for members, replacing those with 'm_data.' is
painless and quick.

Uli

--
Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932


[ 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>
>>> 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
>> }
>> }
>
> It requires a complete rewriting of the members, which is too intrusive.

Not generally. the things you lose are constructors, that are not inherited
currently, and private/public. The latter can be solved by separate 'data'

structs used with the proper keyword on inheritance.
For ctors you must write forwarders by hand. For elaborated classes that is

not likely a big problem, but certainly a pain.

> Instead of mentioning the members in the copy constructor, you have to
> move as many members and update all their uses, so how much does this
> save?

IMO client code does not notice such splitting in any way.

> Moreover, you cannot enforce that all the data members belong to
> BigData and not Big; someone could add a data member to Big and that
> could go unnoticed, couldn't it?

Enforcing correct development is a peopleware and process problem -- that
can be solved by having code reviews on change check in. Certainly if
anyone can hack the code in any way liked without control, the result will
be chaos. And any magic the language gives can be circimvented, suppose you

had some keyword, what prevents it being deleted or #defined away?

While most systems we have today have 'object browser', and it is not hard
to discover things like undue members. (Too bad I didn't see an API to work
with the browse info, then simple scripts run against it could solve that
problem too, and be generally useful)

>



--
[ 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>
> 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.

As later turns out, as debug/development instrumentation, not for the
production code, IUC.

> 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").

I believe you :) just there IIRC this topic was about some general problem
wanting support. If your case -- despite taken from real life -- turns out
to be an exceptional, or knowing its real target, the language has a better
solution, would lead to different conlusions.

>> 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.

Did it actually facilitate debugging? No pun, I'm really curious. I was
(and am) using such tracers, especially in weird environments (like this
moment I'm writing firmware for a PIC system, riddled with interrupts that
prevents use of a 'debugger' for practice, so traces and dumps are the main
tools to see what is happening.)
But I always think of them as a "wart" and they are made either removable by

macros or actually edited out as soon as not needed.
And certainly I wish for things that allow life without them ;)

>> 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.

I'd guess a language like C++ is better tuned for features for production
code. If I were in a committee deciding extension, I'd definitely demand
some use from that territory. As debug aids may seem "hard"
individually, but IMO collapse to a small set of cases with reusable
solutions. Very possibly some tool can be found on the net that generates
code or give a set of something that solves this
'object-lifetime-following'. Just as there are general tools like valgrind
working on a lower level.

> 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.

IMO that is a very good motivation to NOT have the piece of code. What is
not there works good, and even saves read time. Destructors, auto-generated
operators are good. I srtive to keep and use them. The extra code need in

the rule-3 company is a smell, and make me look for rearrangement to use
some member or base class.

Especially, as I too often discovered bugs in the hand-written,
supposed-special-case implementation, something worked bad with exceptions,
or missed some symmetry. Listing all the members is the lightest of the
problems.

>> 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?

IIRC offsetof is a C legacy, and in C++ mandated to work POD only by the
standard. Certainly nothing forbids it working for more cases, and I'm sure
the stock thing is fine for majority of cases, only starts breaking with
some multiple and virtual inheritance cases. The last implementation I saw
was a simple substraction using null pointer. (What the implementation can
know to work fine.)

> I guess there's something inherently unreliable that prevents offsetof
> from being applicable entirely generally.

To me seem like an 'abandoned' thing, as in practice it works where has a
chance, however unofficially, while a full solution, that flag all the
problem cases as errors, AND giving an answer for the rest, and is also
meaningful, considering internal offsets... sounds like another 'not worth
bothering' case.




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