Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: How to write this function about implicit declarations?

72 views
Skip to first unread message

David Brown

unread,
Nov 24, 2017, 7:26:02 AM11/24/17
to
On 24/11/17 12:49, Stefan Ram wrote:
> Can you write a function definition and/or
> extend/improve/correct the parts given below?
>
> auto explain
> ( bool DCu /* whether a default constructor is user-provided */,
> bool CCu /* whether a copy constructor is user-provided */,
> bool CAu /* whether a copy assignment is user-provided */,
> bool MCu /* whether a move constructur is user-provided */,
> bool MAu /* whether a move assignment is user-provided */,
> bool DSu /* whether a destructor is user-provided */ )
> {
> DC = /* 1 when a default constructor will be
> implicitly-declared (or
>
> 2 if this happens, but is deprecated) or
>
> 0 when it will /not/ be implicitly-declared
> (or implicitly-declared as deleted) under the
> conditions described by the parameter values */
>
> CC = /* as above, but for the copy constructor */
> CA = /* as above, but for the copy assignment */
> MC = /* as above, but for the move constructor */
> MA = /* as above, but for the move assignment */
> DS = /* as above, but for the destructor */
>
> return make_tuple( DC, CC, CA, MC, MA, DS ); }
>

I'm not sure what your function is supposed to do here, but I can tell
you some ways to improve it:

1. If you have an abbreviated name that needs a comment, the you could
probably replace it with a better name without a comment.

2. Replace your integer values with an enumerated type.

3. Never return a pair or a tuple. Make it a struct - then people can
use the results sensibly (using the sensible names you are going to
provide) instead of by number.

4. For the benefit of the rest of the programming world, and
/especially/ for the benefit of your students, /please/ switch to a sane
coding style.

Mr Flibble

unread,
Nov 24, 2017, 11:40:39 AM11/24/17
to
+10

> 2. Replace your integer values with an enumerated type.

+1

>
> 3. Never return a pair or a tuple. Make it a struct - then people can
> use the results sensibly (using the sensible names you are going to
> provide) instead of by number.

+1 (you can use std::tie and std::forward_as_tuple if you want to take
advantage of tuple's lexicographical comparison feature)
>
> 4. For the benefit of the rest of the programming world, and
> /especially/ for the benefit of your students, /please/ switch to a sane
> coding style.

+1

All very good answers, 10/10. :)

/Flibble

James R. Kuyper

unread,
Nov 27, 2017, 9:59:39 AM11/27/17
to
On 11/24/2017 07:25 AM, David Brown wrote:
> On 24/11/17 12:49, Stefan Ram wrote:
>> Can you write a function definition and/or
>> extend/improve/correct the parts given below?
>>
>> auto explain
>> ( bool DCu /* whether a default constructor is user-provided */,
>> bool CCu /* whether a copy constructor is user-provided */,
>> bool CAu /* whether a copy assignment is user-provided */,
>> bool MCu /* whether a move constructur is user-provided */,
>> bool MAu /* whether a move assignment is user-provided */,
>> bool DSu /* whether a destructor is user-provided */ )
>> {
>> DC = /* 1 when a default constructor will be
>> implicitly-declared (or
>>
>> 2 if this happens, but is deprecated) or
>>
>> 0 when it will /not/ be implicitly-declared
>> (or implicitly-declared as deleted) under the
>> conditions described by the parameter values */
>>
>> CC = /* as above, but for the copy constructor */
>> CA = /* as above, but for the copy assignment */
>> MC = /* as above, but for the move constructor */
>> MA = /* as above, but for the move assignment */
>> DS = /* as above, but for the destructor */
>>
>> return make_tuple( DC, CC, CA, MC, MA, DS ); }
>>
>
> I'm not sure what your function is supposed to do here,

I suspect that he's indirectly asking about the rules determining
whether or not various special member functions will be implicitly
declared. He's asking that those rules be expressed in the form of C++
logical expressions.

jacob navia

unread,
Nov 27, 2017, 4:02:44 PM11/27/17
to

Le 24/11/2017 à 12:49, Stefan Ram a écrit :
> Can you write a function definition and/or
> extend/improve/correct the parts given below?
>
> auto explain
> ( bool DCu /* whether a default constructor is user-provided */,
This object property is (as far as I know) only visible by the reader of
the program. Either you or the compiler. I can't figure out how a C++
expression could give you that information, since it is in the
declaration of the object, in the source code of the program.

Solutions vary:

1) You hack gcc to write into a file the data you want and you compile
your program with your hacked compiler.
Requisites: You must be a good gcc hacker, know C++ and C very well, etc.

2) You get an executable with your class and read the debug information.
There you will find all answers (maybe).
You can hack the gdb code to extract the debug info processing module
and hack it to print what you want in a file.
Requisites: C language, and some weeks of work. Doable.

3) You can use reflection to build nested templates and preprocessor
macros to iterate through all the fields of the class definition. Then,
you could hack those macros. Look in

http://pfultz2.com/blog/2012/07/31/reflection-in-under-100-lines/

That is a much easier way, since it would only mean to change the macros
to figure out when the constructor is being defined, and if it is not,
you know it is not being defined... I can't do that but maybe is doable.

4) Generate a program for each class you want to investigate, that
includes the right headers and creates an object of that class. Call the
destructor or somehow do an action that can either provoke a compiler
error, or pass compilation. You get the return code of the gcc process
and then interpret it accordingly. That is much easier than all
solutions above, but...

WHY DO YOU WANT TO DO THIS?

Explain.

Explain above all when your function is supposed to run. At compile time
when all this info is available? Or at run time, when it is no longer
visible?


James R. Kuyper

unread,
Nov 27, 2017, 10:41:05 PM11/27/17
to
On 11/24/2017 06:49 AM, Stefan Ram wrote:
> Can you write a function definition and/or
> extend/improve/correct the parts given below?
>
> auto explain
> ( bool DCu /* whether a default constructor is user-provided */,
> bool CCu /* whether a copy constructor is user-provided */,
> bool CAu /* whether a copy assignment is user-provided */,
> bool MCu /* whether a move constructur is user-provided */,
> bool MAu /* whether a move assignment is user-provided */,
> bool DSu /* whether a destructor is user-provided */ )
> {
> DC = /* 1 when a default constructor will be
> implicitly-declared (or
>
> 2 if this happens, but is deprecated) or
>
> 0 when it will /not/ be implicitly-declared
> (or implicitly-declared as deleted) under the
> conditions described by the parameter values */
>
> CC = /* as above, but for the copy constructor */
> CA = /* as above, but for the copy assignment */
> MC = /* as above, but for the move constructor */
> MA = /* as above, but for the move assignment */
> DS = /* as above, but for the destructor */
>
> return make_tuple( DC, CC, CA, MC, MA, DS ); }

I'll handle the first one, DC, to give you an idea of what's actually
involved. I'm basing this off of C++2011.

auto explain
( bool DCu /* whether a default constructor is user-provided */,
bool Cu /* whether any constructor is user-provided */,
bool UVntdc /* Whether it is a union-like class that has a variant
member with a non-trivial default ctor. */,
bool NSDMNIR /* Whether any non-static data member with no
brace-or-equal_initializer is of reference type. */,
bool NVNSDMCQNBDc /* Whether all non-variant non-static data members
of const-qualified type (or array thereof) which have no
brace-or-equal-initializer have default constructors. */,
bool UVMCQ /* Whether it is a union and all members are of
const-qualified type (or array thereof) */,
bool NUMCQ /* Whether it is a non-union class and all members of any
anonymous union member have const-qualified type (or array
thereof) */,
bool BCNSDMNBDc /* Whether all direct or virtual base classes and
non-static data members with no brace-or-equal-initializer have
either a a default constructor, or an applicable overload that is
unambiguous and neither deleted nor inaccessible. */,
...
{
DC = /* 1 when a default constructor will be
implicitly-declared (or

2 if this happens, but is deprecated) or

0 when it will /not/ be implicitly-declared
(or implicitly-declared as deleted) under the
conditions described by the parameter values */
!Cu && // Implicitly declare (12.1p4)
// and not implicitly declared as having been deleted.
!(UVntdc || NSDMNIR || !NVNSDMCQNBDc || UVMCQ || NUMCQ
!BCNSDMNBDc);

I had to add seven arguments to your function, while making no use of
the only argument you probably thought was relevant. I expect that
similar issues would apply to the other four things that you want
calculated, though I haven't bothered checking yet.

Does this even remotely resemble what you're looking for?

It's entirely possible that one or more of the arguments I added is
described or used in exactly the opposite way that it should be - this
is the kind of question where that kind of mistake is easy to make.
However, I hope it's clear what the general framework of this expression
should look like.

David Brown

unread,
Nov 28, 2017, 3:24:51 AM11/28/17
to
On 28/11/17 07:19, Stefan Ram wrote:
> "James R. Kuyper" <james...@verizon.net> writes:
>> I had to add seven arguments to your function, while making no use of
>> the only argument you probably thought was relevant. I expect that
>> similar issues would apply to the other four things that you want
>> calculated, though I haven't bothered checking yet.
>> Does this even remotely resemble what you're looking for?
>
> I was starting to write about constructors for my
> German-language C++ course.
>
> Of course, I wanted to state the conditions under
> which constructors are declared implicitly.
>
> The rules seemed to be complicated. But maybe they
> just /seemed/ to be complicated /to me/, but were
> really quite simple for an expert who might be able
> to give all the rules in just a few lines of text?
>
> So, I had the idea to ask in this newsgroup.
>
> But then I also thought that the English language
> might add some ambiguity or vagueness to rules, and
> that ambiguity or vagueness could be avoided by
> expressing the rules in a more formal manner.
>
> Since we already got C++ as a formal language,
> it would be natural to express the rules using C++
> herself.
>
> From the rest of your reply I take it that the rules
> are so complicated that even for an expert it is
> not simple to write such a function.
>
> Moreover, the rules might change in the future:
> Scott Meyers wrote in 2014:
>
> "At some point, analogous rules may be extended to the
> copy operations, because C++11 deprecates the automatic
> generation of copy operations for classes declaring copy
> operations or a destructor."
>
> Another complication is that in that point of my course, I
> do not have explained all other parts of the language and
> - wishing to avoid forward references - I still have to
> exclude mentioning some parts of the language.
>
> So, in the final end, it might come down to me having to use
> some simplifications in that part of my course and only
> mention some common situations in which certain special
> member functions are generated or are not generated.
>

I would have thought you'd be looking at type traits here. There are
three things you /don't/ want - one is for the user to have to supply a
long list of type attributes manually, when it is easy for them to make
mistakes. The next is to have complex logic like Ben showed you needed,
since it is easy for /you/ to make mistakes. And the third is to have
it all done at run-time instead of compile-time.

I am still not sure of what your use-cases are here. But I think you
want to start with the existing type traits. Then move on to
constructing your own type traits for anything missing. For things that
you can't figure out this way, you then want to ask if it really is a
useful feature.



James R. Kuyper

unread,
Nov 28, 2017, 11:57:59 AM11/28/17
to
Did you mean "James" rather than "Ben"?

> since it is easy for /you/ to make mistakes. And the third is to have
> it all done at run-time instead of compile-time.
>
> I am still not sure of what your use-cases are here.

The use case appears to be exposition: using a C++ logical expression to
express the relevant rules. I don't believe there was any intention to
actually compile and execute this code.

In principle, a conceptually similar function could play a useful role
in a compiler. I've never designed a compiler, so I can't be sure, but
I'd expect that any such function would work in a much different manner
than this one. I'd expect there to be a class with instances that
contain the representation, in the compiler, of a particular type used
in a C++ program. That class would have derived classes for different
categories of types, and either the base class or some of the derived
classes might be templated. For the derived class used to describe class
types, for each special member function that could be implicitly
declared for the described type, there would be a member function which
determines whether it is implicitly declared, and if so, if it is
declared as deleted. That member function would involve different code
depending upon whether the described type was a union, a union-like
class, or non-union. It would involve recursive iteration over the
members of the described class. If any one of the problematic cases
listed in the standard comes up during that iteration, the function is
implicitly declared as deleted.

David Brown

unread,
Nov 29, 2017, 2:46:03 AM11/29/17
to
Yes I did - sorry about that. I don't know why I mixed your names up
here (you both give good advice and good examples, so maybe I had just
read another post by Ben).

>
>> since it is easy for /you/ to make mistakes. And the third is to have
>> it all done at run-time instead of compile-time.
>>
>> I am still not sure of what your use-cases are here.
>
> The use case appears to be exposition: using a C++ logical expression to
> express the relevant rules. I don't believe there was any intention to
> actually compile and execute this code.
>
> In principle, a conceptually similar function could play a useful role
> in a compiler.

I could understand that - but I am fairly confident that Stefan is not
working on a compiler. Simply making a function as an alternative way
to express the rules seems reasonable - perhaps to help with Stefan's
teaching. However, if it is so hard to write the rules, I wonder if it
actually helps. I think Stefan is the only one who could tell us.

> I've never designed a compiler, so I can't be sure, but
> I'd expect that any such function would work in a much different manner
> than this one. I'd expect there to be a class with instances that
> contain the representation, in the compiler, of a particular type used
> in a C++ program. That class would have derived classes for different
> categories of types, and either the base class or some of the derived
> classes might be templated. For the derived class used to describe class
> types, for each special member function that could be implicitly
> declared for the described type, there would be a member function which
> determines whether it is implicitly declared, and if so, if it is
> declared as deleted. That member function would involve different code
> depending upon whether the described type was a union, a union-like
> class, or non-union. It would involve recursive iteration over the
> members of the described class. If any one of the problematic cases
> listed in the standard comes up during that iteration, the function is
> implicitly declared as deleted.

Designing this sort of thing in a compiler is not going to be an easy task!

If /I/ were doing it, on a new compiler, I would try starting by
implementing the basics of the new "metaclass" proposal, using an extra
keyword "__class" instead of "class". "class" and "struct" can then be
implemented as metaclasses - and the rules for which methods are created
by the compiler, as well as a variety of warnings, can all be written in
C++ with metaclasses, rather than in the compiler itself.
0 new messages