Sunday, June 16, 2019

Digest for comp.lang.c++@googlegroups.com - 25 updates in 5 topics

G G <gdotone@gmail.com>: Jun 16 01:04PM -0700

from deitel and deitel C++ how to prrogram
9th edition
 
 
// Fig. 3.1: fig03_01.cpp
// Define class GradeBook with a member function displayMessage()
// create a GradeBook object, and call its displayMessage()
 
#include <iostream>
 
using namespace std;
 
 
// GradeBook class definition
class GradeBook
I
public:
// function that displays a welcome message to the GradeBook user
void displayMessage() const
{
cout << "Welcome to the Grade Book!" << ends;
}
];
 
 
// function main begins program execution
int main()
{
GradeBook myGradeBook;
 
myGradeBook.displayMessage();
}
 
 
My question is about the const use after
displayMessage(). The author notes it should be
used to indicate the function will not change the object
GradeBook. Could you please explain. i'm trying to
understand why it would be necessary, along with
when it would be used again in a program.
Szyk Cech <szykcech@spoko.pl>: Jun 16 10:15PM +0200

> GradeBook. Could you please explain. i'm trying to
> understand why it would be necessary, along with
> when it would be used again in a program.
 
This const will protect you from accident modify GradeBook
object when you implement function GradeBook::displayMessage()
It also tell to class users that it will not modify class.
It can be usefull for testers or programmers from your team.
alexo <alelvb@inwind.it>: Jun 16 05:03PM +0200

Hello,
trying to play a bit with shared pointers I noticed something I don't
understand.
 
In my toy code, here integrally reported, everything seems to work
correctly.
 
My intent is to create a first shared pointer, initialize it, make a
second shared pointer initialize it to make it point to the first
pointer and print the content of the memory location pointed by both to
check if they effectively point to the same block of memory.
 
cut here:
// ---------------------------------------------------------------------
#include <iostream>
#include <memory>
#include <iomanip>
 
using namespace std;
 
void print(const shared_ptr<double[]> & d, size_t n);
 
int main()
{
size_t n {5};
 
shared_ptr<double[]> pdata1 {make_unique<double[]>(n)};
 
auto pdata2 {pdata1};
 
for(size_t i = 0; i < n; i++)
{
pdata1[i] = static_cast<double> (i) / 2;
}
 
print(pdata1, n);
print(pdata2, n);
 
return 0;
}
 
void print(const shared_ptr<double[]> & d, size_t n)
{
for(size_t i = 0; i < n; i++)
{
cout << showpoint << fixed << setprecision(1) << d[i] << " ";
}
 
cout << endl;
}
 
// ---------------------------------------------------------------------
 
I compiled this code with gcc 7.4.0 using the following options:
 
-std=c++17 -pedantic -Wall -Wextra
 
no errors nor warning. Execution shows that the two pointers point to
the same block of memory. But there is a but...
 
To initialize the first pointer I used the following instruction:
 
shared_ptr<double[]> pdata1 {make_unique<double[]>(n)};
 
If I use the function make_shared<> instead, which should be the correct
one, I obtain many error messages, as you can see trying to substitute
the former instruction with the following:
 
shared_ptr<double[]> pdata1 {make_shared<double[]>(n)};
 
 
If instead of an array of doubles I use a single double value...
 
 
cut here
//-----------------------------------------------------------------------
#include <iostream>
#include <memory>
#include <iomanip>
 
using namespace std;
 
int main()
{
shared_ptr<double> pdata1 {make_shared<double>(2.0)};
 
auto pdata2 {pdata1};
 
cout << *pdata1 << endl;
cout << *pdata2 << endl;
 
return 0;
}
 
...everything is ok!
 
What could it be the problem with the double[] type?
 
 
Thank you for your patience and kind attention,
 
alessandro
Melzzzzz <Melzzzzz@zzzzz.com>: Jun 16 03:25PM

> }
 
> ...everything is ok!
 
> What could it be the problem with the double[] type?
 
Probably shared_pointer can't point to array.
 
--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala
alexo <alelvb@inwind.it>: Jun 16 09:07PM +0200

Il 16/06/19 17:25, Melzzzzz ha scritto:
 
>> ...everything is ok!
 
>> What could it be the problem with the double[] type?
 
> Probably shared_pointer can't point to array.
 
It seems to me a little strange, but this is my opinion.
 
Is there a way to check if the memory is released in the array version?
nobody@example.org (Scott): Jun 16 01:58AM

On Sat, 15 Jun 2019 18:49:52 +0200, Bonita Montero
>-declarations. Just right-click at the symbol and click the
>option to guide to the definition.
>I like C++ but I don't think forward-declarations are an adantage.
 
There are ways. Long ago I wrote an assembler that avoided forward
declarations by making two passes over the source. C doesn't do that,
for reasons.
 
C does allow implicit declarations. You can call a previously
undeclared function, and C will trust that the types you're passing
are the types the function's expecting. I think it's a bad idea, and
it usually yields warnings suggesting that it's a bad idea. I think
any form of implicit typing is a bad idea.
Keith Thompson <kst-u@mib.org>: Jun 15 07:31PM -0700

nobody@example.org (Scott) writes:
[...]
> are the types the function's expecting. I think it's a bad idea, and
> it usually yields warnings suggesting that it's a bad idea. I think
> any form of implicit typing is a bad idea.
 
No, it doesn't. Calling an undeclared function has been a constraint
violation since C99 dropped the "implicit int" rule.
 
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
Bonita Montero <Bonita.Montero@gmail.com>: Jun 16 07:28AM +0200

>    fn(a,b,c);
> that either a definition or declaration of fn has been encountered
> prior to this point.
 
There aren't any declarations with .net / Java so there isn't such a
problem.
nobody@example.org (Scott): Jun 16 08:20AM

On Sat, 15 Jun 2019 19:31:13 -0700, Keith Thompson <kst-u@mib.org>
wrote:
 
>> any form of implicit typing is a bad idea.
 
>No, it doesn't. Calling an undeclared function has been a constraint
>violation since C99 dropped the "implicit int" rule.
 
Apparently not a fatal one. gcc 7.4 with -std=c99 warns, but builds it
anyway.
Bonita Montero <Bonita.Montero@gmail.com>: Jun 16 10:54AM +0200

> are the types the function's expecting. I think it's a bad idea, and
> it usually yields warnings suggesting that it's a bad idea. I think
> any form of implicit typing is a bad idea.
 
Missing forward-declarations aren't a kind of implicit typing.
Keith Thompson <kst-u@mib.org>: Jun 16 02:24AM -0700

>>violation since C99 dropped the "implicit int" rule.
 
> Apparently not a fatal one. gcc 7.4 with -std=c99 warns, but builds it
> anyway.
 
As I said, it's a constraint violation. That means that a conforming
compiler must issue a diagnostic message. It's not required to be
fatal. In fact the only construct that required a fatal error is the
"#error" directive. (I personally wish gcc were more strict, but that's
a whole 'nother kettle of fish.)
 
The lesson is that warnings from C compilers should be taken *very*
seriously.
 
"gcc -std=c99 -pedantic" results in a fatal error.
 
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
Keith Thompson <kst-u@mib.org>: Jun 16 02:26AM -0700

>> it usually yields warnings suggesting that it's a bad idea. I think
>> any form of implicit typing is a bad idea.
 
> Missing forward-declarations aren't a kind of implicit typing.
 
I suppose it depends on what you mean by "implicit typing".
 
In C90, this:
 
int main(void) {
some_function();
}
 
was valid, and would implicitly assume that some_function has been
defined with no parameters and a return type of int. (And if that
assumption were violated, you'd have undefined behavior.)
 
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
David Brown <david.brown@hesbynett.no>: Jun 16 12:23PM +0200

On 16/06/2019 03:58, Scott wrote:
> are the types the function's expecting. I think it's a bad idea, and
> it usually yields warnings suggesting that it's a bad idea. I think
> any form of implicit typing is a bad idea.
 
It is worth being clear here that implicit declarations don't let you
use functions properly before declaring them. When a function is used
with an implicit declaration, it is treated as taking an unknown number
of unknown type parameters, using default argument promotions, and
returning an int. If there is a definition (or prototype declaration)
of the function found later in the code, the compiler does not go back
and correct the earlier usage. So as you say, relying on implicit
declarations is a bad idea.
 
 
It is also important to note that C++ is a bit more flexible here. When
declaring a class, functions declared within the class can refer to
members and functions that are declared later in the class. And
template code can refer to identifiers declared later on (as long as
they are declared before the template is invoked).
Bonita Montero <Bonita.Montero@gmail.com>: Jun 16 12:43PM +0200


> int main(void) {
> some_function();
> }
 
In Java / .net that's different because the definition is checked.
nobody@example.org (Scott): Jun 16 05:03PM

On Sun, 16 Jun 2019 02:24:25 -0700, Keith Thompson <kst-u@mib.org>
wrote:
 
 
>The lesson is that warnings from C compilers should be taken *very*
>seriously.
 
>"gcc -std=c99 -pedantic" results in a fatal error.
 
Hmm. Which gcc are you using? Mine (7.4.0) still gives a warning with
those flags. -pedantic-errors does give an error as I expected. I'm
not saying you're wrong, I'm just curious why we're seeing different
results with the same experiment.
 
The source snippet I used is this:
 
int main(void) { return 45 != foo("Hello", 2.5, 45); }
int foo(char *x, double y, int z) { return z; }
nobody@example.org (Scott): Jun 16 05:05PM

On Sun, 16 Jun 2019 12:43:45 +0200, Bonita Montero
>> some_function();
>> }
 
>In Java / .net that's different because the definition is checked.
 
[checks Newsgroups header]
[notes presence of C / C++ newsgroups]
[notes lack of Java / .net newsgroups]
David Brown <david.brown@hesbynett.no>: Jun 16 11:55AM +0200

On 15/06/2019 20:34, Alf P. Steinbach wrote:
 
>> True (for C++14 onwards).
 
> `std::greater` was there, providing a total ordering, in the first C++
> standard, C++98. It's necessary for ordered containers of pointers.
 
Yes. (I misread a detail in a reference page - it wasn't added in
C++14, merely slightly modified.)
 
> version, the 286, would be employed for segment handling. There were no
> segment tables, just a simple arithmetic relationship (namely 16x)
> between selector and physical segment start address.
 
The architecture was called "segmented", whether you like the name or
not (and I tend to agree with you here).
 
As a more general point, systems can have multiple views of memory and
multiple ways to order it, and may not have a complete order for
addresses. Logical and physical addresses can have very different
mappings. You can have aliasing with different logical addresses
mapping to the same physical address. You can have the same logical
address mapping to multiple physical addresses in different
circumstances. You can have all sorts of combinations.
 
It can certainly be useful to have an ordering on addresses for things
like containers - all you need is an ordering, without any requirement
for it to be "real" in any sense. But it does not let you get
information about sizes of stacks or memories - each object is, in
effect, floating "somewhere" unconnected to any other object.
 
David Brown <david.brown@hesbynett.no>: Jun 16 12:13PM +0200

On 15/06/2019 20:41, Alf P. Steinbach wrote:
>> different from any other code.
 
> In the context of tail recursion a destructor is different, in that it's
> invoked implicitly, not explicitly visible in the source code.
 
In the source code, it is invoked implicitly, yes (that's the whole idea
of them). But as far as the compiler is concerned, when it translates
the source code into internal formats it fills in the destructor call
just like any other function invocation. /Then/ it starts to analyse
and manipulate the code, looking for optimisations such as tail recursion.
 
Destructors, being hidden in the source, might make it harder for
programmers to guess if a particular function is likely to be suitable
for tail recursion optimisation - but it makes no difference to the
compiler.
 
> Visual C++ stubbornly refused to optimize tail recursion floating point
> type results. So if one relies on tail recursion in C++ one should
> better know what one is doing, and know one's compilers.
 
Exactly, yes.
 
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Jun 16 11:35AM +0100

On Sat, 15 Jun 2019 17:22:00 +0200
> may be inlined. If the compiler can arrange the recursive calls with
> tail call optimisations, it will - destructors are not any different
> from any other code.
 
Yes, but generally it can't do this, because destructors may only be
called at the end of the local scope in which the object concerned has
its lifetime, and in the reverse order of construction of other objects
in that scope. Any observable effect of object destruction, as required
by the C++ standard, must occur after the recursive function call.
 
The result of that is that a recursive function call at the end of local
scope, which might appear to be in tail position, in fact isn't - the
destructors still remain to execute. I cannot see how an optimizer can
get around this where the objects concerned are other than trivial (in
the C++-standard sense of that word).
 
The proof of the pudding is in the eating: can you actually produce some
code where tail call elimination is applied with non-trivial objects in
local scope? If you can, a few lines of code should suffice to
demonstrate this.
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Jun 16 12:10PM +0100

On Sun, 16 Jun 2019 11:35:40 +0100
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
[snip]
> code where tail call elimination is applied with non-trivial objects in
> local scope? If you can, a few lines of code should suffice to
> demonstrate this.
 
The kind of case to which I am referring is this:
 
void recfunc() {
std::string s;
... do something ...
recfunc();
}
 
You can bring back tail recursion by massaging the code:
 
void recfunc() {
{
std::string s;
... do something ...
}
recfunc();
}
 
But in a case where recfunc() takes an argument, you cannot easily use
the string to provide an argument for the recursive function, because
it is no longer in scope at the time of the recursive call.
David Brown <david.brown@hesbynett.no>: Jun 16 01:55PM +0200

On 16/06/2019 12:35, Chris Vine wrote:
> destructors still remain to execute. I cannot see how an optimizer can
> get around this where the objects concerned are other than trivial (in
> the C++-standard sense of that word).
 
Of course a compiler can't do magic - if the operation of the destructor
is too complicated to re-arrange to get tail recursion optimisation,
then you don't get the optimisation. My point - and I hope this is the
last time I have to write the same thing here - is that destructors are
not special. They are just like any other function invocation. If they
are simple enough, or if the compiler can figure out how to re-arrange
things - then they don't hinder the optimisation. (And many destructors
/are/ simple.) Destructors don't have to be called at the end of the
scope or in reverse order of construction - but their observable effects
must occur exactly as if they had been called in that place and order.
Just like any other function call.
 
Destructors that do complex things, like deallocate memory and
resources, make it unlikely that a compiler can do tail recursive
optimisations. But it is nothing special about destructors - it is the
same as any other code.
 
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jun 16 05:00PM +0200

On 16.06.2019 13:10, Chris Vine wrote:
 
> But in a case where recfunc() takes an argument, you cannot easily use
> the string to provide an argument for the recursive function, because
> it is no longer in scope at the time of the recursive call.
 
The following shows what the compiler would have to do.
 
I am not sure if it's /allowed/ to do such a rewrite.
 
(To make this compile without the referred library, if that's desired,
just #include the necessary standard library headers and replace
`$use_std` with `using`-declarations.)
 
 
----------------------------------------------------------------------
#include <cppx-core/all.hpp> // <url:
https://github.com/alf-p-steinbach/cppx-core>
 
namespace my{
$use_std( clog, endl, ostringstream, setw, stack, string );
 
class Logger
{
static inline int level = 0;
 
string m_indent;
string m_funcname;
string m_arglist;
 
public:
~Logger()
{
--level;
clog << m_indent << "< " << m_funcname << m_arglist << endl;
}
 
template< class... Args >
Logger( const string& funcname, const Args&... args ):
m_indent( 4*level, ' ' ),
m_funcname( funcname )
{
if constexpr( sizeof...( args ) > 0 ) {
ostringstream stream;
stream << "(";
auto output = [&stream, n = 0]( auto v ) mutable -> int
{
if( n > 0 ) { stream << ", "; }
stream << v;
++n;
return 0;
};
const int a[] = { output( args )... }; (void)a;
stream << ")";
m_arglist = stream.str();
}
clog << m_indent << "> " << m_funcname << m_arglist << endl;
++level;
}
};
 
auto recursive_gcd( const int a, const int b )
-> int
{
const Logger logging( __func__, a, b );
const int r = a % b;
if( r == 0 ) { return b; }
return recursive_gcd( b, r );
}
 
auto tail_optimized_gcd( int a, int b )
-> int
{
stack<Logger> logging_stack;
for( ;; )
{
logging_stack.emplace( __func__, a, b );
const int r = a % b; // const int r = a % b;
if( r == 0 ) // if( r == 0 ) {
return b; }
{
while( not logging_stack.empty() ) { logging_stack.pop(); }
return b;
}
a = b; b = r; // return
recursive_gcd( b, r );
}
}
 
} // namespace my
 
namespace app{
$use_std( cout, endl );
 
void run()
{
cout << "Recursive:" << endl;
const int r_gcd = my::recursive_gcd( 60, 16 );
cout << "recursive_gcd(60, 16) = " << r_gcd << endl;
cout << endl;
cout << "Tail-optimized:" << endl;
const int to_gcd = my::tail_optimized_gcd( 60, 16 );
cout << "tail_optimized_gcd(60, 16) = " << to_gcd << endl;
}
 
} // namespace app
 
auto main() -> int { app::run(); }
----------------------------------------------------------------------
 
 
Output:
 
Recursive:
> recursive_gcd(60, 16)
> recursive_gcd(16, 12)
> recursive_gcd(12, 4)
< recursive_gcd(12, 4)
< recursive_gcd(16, 12)
< recursive_gcd(60, 16)
recursive_gcd(60, 16) = 4
 
Tail-optimized:
> tail_optimized_gcd(60, 16)
> tail_optimized_gcd(16, 12)
> tail_optimized_gcd(12, 4)
< tail_optimized_gcd(12, 4)
< tail_optimized_gcd(16, 12)
< tail_optimized_gcd(60, 16)
tail_optimized_gcd(60, 16) = 4
 
 
Irrelevant to the question of tail optimization, but I was surprised
that one had to manually clear the stack to get LIFO destruction. Hm!
 
 
Cheers!,
 
- Alf
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Jun 16 04:08PM +0100

On Sun, 16 Jun 2019 13:55:59 +0200
> then you don't get the optimisation. My point - and I hope this is the
> last time I have to write the same thing here - is that destructors are
> not special.
 
I never said they were special. I said that in C++ they generally
remove the ability of the compiler to perform tail call elimination
where non-trivial objects are in local scope when the recursive call is
made, because that call is (contrary to appearances) not in tail
position. I was right. Insofar as you are saying otherwise, I think you
are wrong. I hope this is the last time I have to write the same thing
here.
 
But you now state that you are saying something else. Me: "Cooked
apples are best served with custard." You: "No, pears are best served
with cream."
 
> > code where tail call elimination is applied with non-trivial objects in
> > local scope? If you can, a few lines of code should suffice to
> > demonstrate this.
 
To continue the culinary analogy, I still await some code, if you say
the case exists. (But I think you are now saying that you agree after
all with the point I made three posts up-thread.)
Szyk Cech <szykcech@spoko.pl>: Jun 16 11:38AM +0200

Hi!
I think my post scare him/them enough:
snews://newshosting.com:563/mTxKE.510937$2A5.222361@fx10.fr7
To avoid more posts like this they decite to wipe out him from this
group. I think...
 
 
>     cout << endl;
> }
> <======================================================================>
 
There is a litle bug: last word will be end with " " (space) instead "."
dot.
 
best regards
Szyk Cech
rick.c.hodgin@gmail.com: Jun 16 03:46AM -0700

On Sunday, June 16, 2019 at 5:38:28 AM UTC-4, Szyk Cech wrote:
> Hi!
> I think my post scare him/them enough:
 
Your posts do not scare me. I do fear for you though. Your blindness
and arrogance and pride harm you greatly.
 
> To avoid more posts like this they decite to wipe out him from this
> group. I think...
 
God is getting ready to move in this world. You've all been taught
the message. You've heard the truth. You've either received or
rejected it.
 
Look at the changes in our society. The whole world has turnef
against Jesus Christ.
 
Time is almost up fir this age of the gentiles. Next up is the
rapture, and the seven year Tribulation begins.
 
--
Rick C. Hodgin
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: