Monday, July 23, 2018

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

Tim Rentsch <txr@alumni.caltech.edu>: Jul 18 11:49AM -0700

>> practice arrays/vectors nearly always do better.
 
> That's completely true, but in this case it may be a micro-optimization,
> even premature optimization, with little to no benefit.
 
The (snipped) suggestion is to consider, and possibly investigate,
an alternate approach. Considering and possibly investigating is
not optimization, either micro-, premature, or otherwise.
 
 
> The advantage of using std::list is that the code becomes simpler,
> and thus the likelihood of bugs is smaller.
 
> Know your tools, and use them efficiently.
 
I don't want to be rude, but you are taking a lecturing tone
with people whose understandings are deeper than your own.
Paavo Helde <myfirstname@osa.pri.ee>: Jul 16 03:45PM +0300

On 16.07.2018 13:20, Bart wrote:
>> using cppx::Range_;
 
>> const auto identstarter = Set_<char>{}
>> + Range_{ 'A', 'Z' } + Range_{ 'a', 'z' } + '_' + '$';
[...]
 
> But what is the underlying data-structure? Pascal-like sets might use a
> bitmap so the test for 'in' is a simple bit-test (after checking for
> range and locating the byte where the bit will be).
 
Alf's Set_ is a template probably taking any type which has a comparison
operator defined. C++ has std::bitset which could be used automatically
for a limited range datatype like char. Not sure if this actually a case
or if it would be a good idea at all, too much automatism is not always
good either. Maybe define another class like LimitedRangeSet or something.
 
For comparison, here is a solution using only C++ standard classes. It
requires some helper functions to simplify the usage. As Alf said, C++
tends to give you the building blocks, not the ready-made solution. In a
proper C++ solution these functions should probably go into a custom
class for more encapsulation, this is just a minimal example.
 
#include <iostream>
#include <bitset>
#include <limits>
 
constexpr char kMin = std::numeric_limits<char>::min();
constexpr char kMax = std::numeric_limits<char>::max();
using set = std::bitset<kMax-kMin+1>;
 
set Range(char a, char b) {
set x;
while (a<=b) {
x.set(a++ - kMin);
}
return x;
}
 
set Elem(char a) {
return Range(a, a);
}
 
bool In(char c, const set& x) {
return x[c-kMin];
}
 
int main() {
set identstarter =
Range('A', 'Z') | Range('a', 'z') | Elem('_') | Elem('$');
 
set identbody = (identstarter | Range('0', '9')) & ~Elem('Z');
// exclude Z
 
char c = 'Z';
if (In(c, identstarter)) {
std::cout << c << " is in identstarter\n";
}
if (In(c, identbody)) {
std::cout << c << " is in identbody\n";
}
}
bitrex <user@example.net>: Jul 19 01:32PM -0400

> bothered about calling free then just use alloca() and allocate on the stack.
> No free() required though there may be limits on how much memory the OS or
> CPU will allow.
 
alloca() is non-portable, and some implementations are bugged. "May be
limited" in practice means "Always by default very limited on all modern
PC architectures and OSes."
 
Bo Persson <bop@gmb.dk>: Jul 18 12:14PM +0200

On 2018-07-18 09:50, Tim Rentsch wrote:
 
> I wasn't offering any opinion about how much C++ is to write home
> about. My comment is only about the laughable proposition that
> C++-minus-templates is only mildly larger than C.
 
Yeah! One data point:
 
K&R "The C Programming Language" - 200 pages
 
Stroustrup "The C++ Programming Language" - 1300 pages
 
 
 
Bo Persson
Tim Rentsch <txr@alumni.caltech.edu>: Jul 18 12:48AM -0700


>> "a mild superset". That's a good one. :)
 
> It is better to say that C++ has compatibility with C, then speaking in
> terms of superset.
 
My comment was not about the superset part, only about the
mild part.
Tim Rentsch <txr@alumni.caltech.edu>: Jul 18 12:50AM -0700


> Without metaprogramming (and all the stuff that utilizes template
> metaprogramming like the STL) C++ really isn't much to write home
> about in the year of our Lord 2018.
 
I wasn't offering any opinion about how much C++ is to write home
about. My comment is only about the laughable proposition that
C++-minus-templates is only mildly larger than C.
Bart <bc@freeuk.com>: Jul 16 05:33PM +0100

On 16/07/2018 15:59, Paavo Helde wrote:
>>      subelem(identbody,'Z');
 
> I notice this is 10 lines/statements of *usage* in C as compared to the
> 2 lines in C++,
 
Unless it's ultra-simple like the script version, multiple simple lines
can be better than cramming more stuff on one line. But if the line
count matters, then the C can be tweaked so that addrange() etc return
their set parameter. Then it's three lines (plus declarations):
 
addelem(addelem(addrange(addrange(identstarter,'A','Z'),
'a','z'),'_'),'$');
memcpy(identbody,identstarter,sizeof(Set));
subelem(addrange(identbody,'0','9'),'Z');
 
Or two lines if changed to pass and return sets by value.
 
But to go back to an earlier point, this sort of stuff really belongs in
a higher level language (higher level than C++), otherwise the use of it
seems laboured. And the implementation of it (I've glanced at Alf's
headers) has to be reprocessed on each build too.
 
For example, you wouldn't write this even in C++:
 
if (!In(c, Elem(cr) | Elem(lf) | Elem(tab) | Elem(space)) ...
 
like my other script example, because it would be over the top. (And you
can sense the amount of machinery required to make it work, even if it
can eventually be optimised down to little). You would write this:
 
if (!(c == cr || c == lf || c == tab || c == space)) ...
 
which also has short-circuit evaluation as well as being less likely to
have people scratching their heads.
 
(FWIW I don't try to emulate such sets when writing at the level of C. I
use character maps (char[256]) directly without pretending they are
sets. They are initialised via a loop over 0 to 255 so the issue of a
set constructor doesn't arise.
 
As I also said elsewhere you adapt to the language.
 
Having said that, fixed-size sets using bitmaps (and Ranges as a
separate type) /could/ be built-in to a C-type language, and the
handling of them can be made lightweight enough that they could be used
with spontaneity and with little overhead, especially when the set fits
into a machine word.
 
But I haven't done that myself because I don't write enough code in such
a language; I would use the next one up!)
 
--
bart
Juha Nieminen <nospam@thanks.invalid>: Jul 18 07:53AM

> A good suggestion IMO. A problem with lists (as I think Bjarne has
> pointed out) is they suffer from lack of cache locality, so in
> practice arrays/vectors nearly always do better.
 
That's completely true, but in this case it may be a micro-optimization,
even premature optimization, with little to no benefit.
 
I this were a number-crunching example, where the list of elements is
being traversed and modified millions of times per second, as fast as
the computer can possibly do it, I would never use a linked list
(unless it's absolutely necessary, which is the case with a few
algorithms; rarely, but they exist).
 
However, this example is a list of a couple dozen elements, give or take,
which is traversed and modified once per frame (ie. typically 60 times
per second). It's hardly a bottleneck.
 
Moreover, depending on the particular implementation, the projectiles
themselves may be dynamically allocated objects (which is very common
with many game engines, where sprites are typically dynamically
allocated), so you are not really saving a lot of dynamic allocations
by not using a linked list.
 
The advantage of using std::list is that the code becomes simpler,
and thus the likelihood of bugs is smaller.
 
Know your tools, and use them efficiently.
Bart <bc@freeuk.com>: Jul 16 03:27PM +0100

On 16/07/2018 13:45, Paavo Helde wrote:
> }
 
> bool In(char c, const set& x) {
>   return x[c-kMin];
 
In general this needs a range check, as you want indices outside the
span of the set to return 0. Here you've arranged the index to be an
char type with the same limits as the set.
 
The problem is that someone might pass 1000000, which is silently
narrowed to char (is it, in C++?), so then it becomes 64. Which is not
in the set. But 1000001 (65) apparently will be. This is part of a more
general problem, but using narrow function parameters, it's just more
likely to go wrong.
 
>     std::cout << c << " is in identbody\n";
>   }
> }
 
You can simplify it further. Below is a version in C that uses byte-maps
rather than bit-maps, which is OK for small sets. Apart from the
function syntax, it's not so objectionable.
 
However this was simple because I've used a fixed Set type (and assumed
that 'char' is unsigned and that character codes are ASCII). Below the C
code, is reminder of the script version.
 
There, the feature is often used trivially just to do a multiple compare:
 
while ch not in [cr, lf, space, tab] do ...
 
(With byte-code, this can be faster than doing up to four lots of compares.)
 
Expressivity at work...
 
----------------------------------
// C code
 
#include <stdio.h>
#include <string.h>
 
typedef char Set[256];
 
Set identstarter;
Set identbody;
 
void clearset(Set e){
memset(e,0,sizeof(Set));
}
 
void addrange(Set e,int a,int b){
while (a<=b) e[a++]=1;
}
 
void addelem(Set e,int a){ // these should be range-checked, but a bad
e[a]=1; // value is a coding error; passing out-
// out-of-range index to in() is OK
}
 
void subelem(Set e,int a){
e[a]=0;
}
 
int in(int a, Set e){
if (a<0 || a>=256) return 0;
return e[a];
}
 
void printset(char* caption,Set e){
int i;
printf("%s = [",caption);
for (i=0; i<256; ++i)
if (e[i])
if (i>32 && i<127)
printf("%c ",i);
else
printf("0x%02X ",i);
puts("]");
}
 
int main(void) {
int c;
 
clearset(identstarter);
addrange(identstarter,'A','Z');
addrange(identstarter,'a','z');
addelem(identstarter,'_');
addelem(identstarter,'$');
 
memcpy(identbody,identstarter,sizeof(Set));
addrange(identbody,'0','9');
subelem(identbody,'Z');
 
printset("Identstarter",identstarter);
printset("Identbody",identbody);
 
c='Z';
printf("%d\n", in(c, identstarter));
printf("%d\n", in(c, identbody));
}
 
---------------------------------
proc start=
identstarter := ['A'..'Z', 'a'..'z', '_', '$']
identbody := identstarter + ['0'..'9'] - ['Z']
 
c:='Z'
println c in identstarter
println c in identbody
 
end
 
--
bart
Manfred <noname@add.invalid>: Jul 16 03:12PM +0200

On 7/16/2018 2:51 PM, Scott Lurndal wrote:
> and understanding the difference between production code and whatever
> a debug build might be (none of us program on windows systems or use
> VS - for us, a debug build omits -O3).
 
Still one question remains: was it unique_ptr or shared_ptr's that you
removed (or both)?
The difference is relevant.
That said, blindly replacing all raw pointers with smart pointers is a
misuse, I agree with you need to know what you are doing and choose
appropriately.
 
(by the way, you know that with gcc omitting -O3 is equivalent to -O0)
 
jacobnavia <jacob@jacob.remcomp.fr>: Jul 18 09:54PM +0200

Le 18/07/2018 à 11:16, Ian Collins a écrit :
 
> Old clang?
 
Exactly. That was the problem. Thanks Ian.
jacob
Soviet_Mario <SovietMario@CCCP.MIR>: Jul 17 03:38AM +0200

Il 15/07/2018 18:34, Paavo Helde ha scritto:
 
> In C++ the only sensible way to have GC is to logically
> think that all allocated objects are leaked, but that's ok
> as you have got infinite memory.
 
Uh ... what do you actually mean with this "you have
infinite memory" ?
To use it up freely, never caring, and to simply rely on
try/catch/finally in case some error occurs ?
 
> Behind the scenes the
> memory gets reused of course,
 
again I can't understand.
Do you mean at the OS' memory management level ?
Or by some "hidden" code generated by modern compilers ?
Or even sth else ...
 
> released properly and are not left to GC which will run in
> unpredictable times in a random thread.
 
> I believe the current consensus is that with RAII one can
 
at the very outdated time I firstly read of RAII, memory
allocation was one of such resources. Why this distinction now ?
 
> control both memory and non-memory resources in the same way
> and with less hassle, so RAII all the way down it is. Thus
> GC is effectively not needed in C++.
 
SNIP
 
ah, another question ... when you speak of POOLS, you mean
allocating on the heap (or even statically) a big contiguous
chunk and then overload new/delete into more specialized
(and faster) forms which pick up some space within that chunk ?
 
Ciao
 
 
--
1) Resistere, resistere, resistere.
2) Se tutti pagano le tasse, le tasse le pagano tutti
Soviet_Mario - (aka Gatto_Vizzato)
Vir Campestris <vir.campestris@invalid.invalid>: Jul 16 09:20PM +0100

On 16/07/2018 13:52, Andrew Goh wrote:
> thanks for all the responses, actually coming from the java world c++ is quite as 'native' to me, i'd guess most 'java programmers' (and so do other similar managed languages) are somewhat spoilt by garbage collection.
 
I have a satnav that runs Android, so the app will be Java.
 
Every few weeks I have to reboot it. It usually goes deaf (ignores voice
control); sometimes it doesn't switch between day and night correctly;
and every so often it plain crashes and burns. To me it's obviously got
some kind of resource leak.
 
Andy
Rosario19 <Ros@invalid.invalid>: Jul 17 11:09AM +0200

On Mon, 16 Jul 2018 21:20:24 +0100, Vir Campestris wrote:
 
>and every so often it plain crashes and burns. To me it's obviously got
>some kind of resource leak.
 
>Andy
 
the leak can be in C++ even if there are no leak,
if the allocator (malloc()/free() or new()/delete()) not has one algo
for return to OS the memory (zeroed first) that can
Rosario19 <Ros@invalid.invalid>: Jul 17 11:10AM +0200

On Tue, 17 Jul 2018 11:09:21 +0200, Rosario19 wrote:
 
>the leak can be in C++ even if there are no leak,
>if the allocator (malloc()/free() or new()/delete()) not has one algo
>for return to OS the memory (zeroed first) that can
 
the leak can be in C++ even if there are no leak,
if the allocator (malloc()/free() or new()/delete()) not has one algo
for free to OS the memory (zeroed first) that can
Jeff-Relf.Me @.: Jul 23 03:12AM -0700

boltar@cylonHQ.com: Jul 17 09:03AM

On Mon, 16 Jul 2018 19:31:37 +0300
>> and Windows 10 is no different.
 
>There are lots of computers where there is no C:\Program Files, but for
>example C:\Programme or C:\Archivos de programa.
 
Thats just fuckwittery on the part of Microsoft. You wouldn't go into a
unix install in france or spain and expect to see /maison or /casa instead of
/home. Major OS paths should be standardised no matter what the locale.
Tim Rentsch <txr@alumni.caltech.edu>: Jul 18 12:47AM -0700

> I guess the case of having side effects qualifies as not a "core
> constant expression" which in n4659 10.1.5 p5 results in an ill-formed
> program, without warning (I hate those)
 
My reading of this passage is that the program is ill-formed only
if the function cannot possibly be called to deliver a constexpr
result. As long as there is some way of calling it to deliver a
constexpr result, it's okay to call it other ways that result in
runtime evaluation. Yes?
scott@slp53.sl.home (Scott Lurndal): Jul 20 01:10PM

>>> essentially the same environment I had in 1988.
 
>> If it works for them, why should they change?
 
>30 years ago I was writing C. C worked for me, why should I change?
 
False analogy.
 
In any case, there is quite a bit of C code still being developed
and maintained.
legalize+jeeves@mail.xmission.com (Richard): Jul 20 08:51PM

[Please do not mail me a copy of your followup]
 
Vir Campestris <vir.campestris@invalid.invalid> spake the secret code
 
>30 years ago I was writing C. C worked for me, why should I change?
 
If your needs haven't progressed beyond what you were writing 30 years
ago, then no need to change. A current example of where that might be
the case is coding for small embedded processors. There is limited
memory and ROM storage space, so the kinds of situations where large
scale problems appear just don't occur.
 
However, I find myself working in larger problem spaces now and C
doesn't scale well to larger code bases because of its limited ability
to express abstractions directly in code.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Terminals Wiki <http://terminals-wiki.org>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>
boltar@cylonHQ.com: Jul 19 03:08PM

On Thu, 19 Jul 2018 14:29:52 GMT
> public c_rx_src_interface,
> public c_memory_accessors
 
>class c_zyx: public c_device,
 
I hope your real class names are better than c_xyz and c_zyx!
 
>to that - it automatically creates a thread and invokes the
>derived class 'run' function from that thread after the
>derived class constructor completes).
 
And if the derived class constructor doesn't complete but throws an exception?
Presumably you have to tell the thread not to bother in the exception handler.
That seems a rather obtuse and error prone way to obtain a new thread.
Tim Rentsch <txr@alumni.caltech.edu>: Jul 18 11:16AM -0700

> easily becomes a mess...
 
> I know it's a matter of personal preference (or rules agreed by the team),
> but I'm curious what your preference is, and why.
 
I don't have any hard-and-fast rule. Generally, though, I find
that indenting nested constructs becomes counterproductive when
what is being nested exceeds some length. I don't know what
that length is exactly but for concreteness let me call it 100
lines. Below threshold, especially when what is being nested
fits on a single 50- or 60- line page, indenting is okay. Above
threshold, indenting hurts more than it helps, and it's better
just not to indent at all.
 
Related comment: any nesting construction where what is being
nested exceeds some relatively small amount (50 lines, say)
deserves a comment on its end marker indicating what is being
closed. I recommend this practice whether or not the nested
portion is indented.
"Chris M. Thomasson" <invalid_chris_thomasson@invalid.invalid>: Jul 17 10:06PM -0700

On 7/17/2018 3:19 PM, Vir Campestris wrote:
>     }
> }
 
> I seem to be in a minority.
 
I have indented them all before. It does seem to have potential scaling
issues wrt deeply nested namespaces.
Ian Collins <ian-news@hotmail.com>: Jul 18 06:34PM +1200

On 18/07/18 17:06, Chris M. Thomasson wrote:
 
>> I seem to be in a minority.
 
> I have indented them all before. It does seem to have potential scaling
> issues wrt deeply nested namespaces.
Mitigate by sticking to two space indentation, or use nested namespace
as Flibble showed.
 
--
Ian
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jul 20 05:06AM +0200


> I have two scenarios:
> - Enforce at compile time that an object can only be instanciate on
> the stack. This is to implement a "safe" dynarray type like object.
 
Since you mention elsewhere that the /purpose/ is to use stack
allocation like a VLA or alloca, there is as far as I know no portable
way to differentiate whether an object is allocated in automatic memory,
as a static variable, or as part of an array or class type object that
in turn can be allocated anywhere.
 
Semi-portably you might compare the object's address to an address
obtained in `main`, assuming that the stack has some maximum size (that
you might infer from a preprocessor symbol) and that it grows down in
memory (again probably best to let the build commands specify).
 
That approach gives you runtime checking.
 
As a convenience measure, getting some ungood usage errors up front at
compile time, I would then "= delete" the class' allocation functions,
`operator new` and `operator new[]`.
 
 
> - Enforce at compile tiem that an object can only be instanciate on
> the heap. This is to ensure That no "Big" object are instanciated > on the stack.
 
I would make the destructor non-public, i.e. implement the restriction
with one or two lines:
 
protected:
virtual ~Dynamic_only() {}
 
That works nicely with e.g. `std::make_shared`, at the cost of having to
use a simple wrapper function. It doesn't work quite so nicely with
`std::make_unique`, requiring per-class boilerplate code. But doable, as
exemplified below.
 
The `std::make_shared` support below, the `std::make_shared` wrapper,
can easily be moved to a reusable mix-in class, I think.
 
Note: the FAQ instead recommends making the constructors non-public and
defining factory functions. In C++03 that wasn't so practical, requiring
one factory function per constructor. And so I argued a bit with
Marshall Cline (the FAQ maintainer at the time), to change the FAQ to
recommend the non-public destructor approach, but to no avail.
 
However, with C++11 we now have ~perfect argument forwarding, so that
the umpteen factory functions of C++03 can be rolled into one per class,
so that the FAQ's advice isn't so impractical anymore. :) So that's an
alternative. However I like being able to use the usual instantiation
code instead of calling factory functions.
 
 
----------------------------------------------------------------------
#include <memory> // std::make_shared
#include <type_traits> // std::is_base_
#include <utility> // std::(enable_if_t, forward)
using namespace std;
 
template< class T > using ptr_ = T*;
template< class T > using ref_ = T&;
 
class Dynamic_only
{
protected:
virtual ~Dynamic_only() {}
 
public:
Dynamic_only() {}
Dynamic_only( const int, const int, const int ) {}
 
// A default unique_ptr doesn't store a unique deleter, so one must
// use indirection via a specialization of std::default_delete. This
// works for direct use of this class, but a derived class needs to
// duplicate the effort. std::unique_ptr is just deleter-challenged,
// which is a cost of zero overhead for the default instantiation.
static void destroy( ptr_<Dynamic_only> p ) { delete p; }
 
// A default shared_ptr stores a unique deleter obtained at the point
// where the shared_ptr is constructed, e.g. in the return stm below:
template< class... Args >
static auto make_shared( Args&&... args )
-> shared_ptr<Dynamic_only>
{
struct Accessible: Dynamic_only
{
using Dynamic_only::Dynamic_only; // Constructors.
~Accessible() override {}; // Just to be explicit.
};
 
return std::make_shared<Accessible>( forward<Args>( args )... );
}
};
 
#ifndef TEST_UNSUPPORTED_MAKE_UNIQUE
// Annoyingly verbose support for std::unique_ptr:
namespace std
{
template<>
struct default_delete<Dynamic_only>
{
constexpr default_delete()
noexcept
{}
 
template< class U
, class = enable_if_t< is_convertible_v< ptr_< U >, ptr_<
Dynamic_only > > >
 
default_delete( ref_<const default_delete<U>> )
noexcept
{}
 
void operator()( const ptr_<Dynamic_only> p ) const
{
Dynamic_only::destroy( p );
}
};
} // namespace std

No comments: