Saturday, April 13, 2019

Digest for comp.lang.c++@googlegroups.com - 16 updates in 4 topics

bitrex <user@example.net>: Apr 13 12:42PM -0400

So there's static polymorphism using e.g the CRTP that allows
logic customization of derived types of an interface at compile time,
but does not allow for dynamic dispatch. This has low (in theory zero)
overhead as compared to runtime polymorphism.
 
And there's virtual methods which allow for dynamic dispatch of
overloaded derived class methods through a base class pointer, in theory
to an "infinite" number of overloads in various derived classes. And has
the usual vtable + dynamic dispatch overhead.
 
What I'm wondering is if there's some known hybrid approach that allows
dynamic dispatch-type behavior from a base type pointer, with the lower
overhead of static polymorphism, given the constraint (has to be a
constraint somewhere I guess or you're just talking about regular
virtual based runtime polymorphism, again) that it works only with a
limited subset of potential subclasses designed expressly for that purpose.
 
Or some other fashion of constraint.
 
Or would any solution of that type simply also be user-implemented
virtualization "in disguise." I have read a bit about the "Visitor"
pattern which seems relevant to my question but don't know enough to
know for sure. Thanks
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Apr 13 08:34PM +0200

On 13.04.2019 18:42, bitrex wrote:
> logic customization of derived types of an interface at compile time,
> but does not allow for dynamic dispatch. This has low (in theory zero)
> overhead as compared to runtime polymorphism.
 
CRTP can give code duplication overhead.
 
In the early days that was a real issue, and one wrote template classes
with strict client side type checking as wrappers around a single
`void*` state based implementation. Whose code was not duplicated.
 
As I recall it was Andrei Alexandrescu who first noted, somewhere in the
late 200x?, that compilers had progressed to a point where code
duplication, template code bloat, was no longer a serious issue.
 
 
> virtual based runtime polymorphism, again) that it works only with a
> limited subset of potential subclasses designed expressly for that purpose.
 
> Or some other fashion of constraint.
 
One can always dispatch on a class id.
 
But that's an application level indirection, to avoid a core language
level indirection, so not likely to be faster.
 
 
> virtualization "in disguise." I have read a bit about the "Visitor"
> pattern which seems relevant to my question but don't know enough to
> know for sure. Thanks
 
The visitor pattern just avoids or (depending on the case) centralizes
downcasting.
 
Again there's Andrei: he wrote at length about visitor pattern variants
in his now classic Modern C++ Design book.
 
 
 
Cheers!,
 
- Alf
Marcel Mueller <news.5.maazl@spamgourmet.org>: Apr 13 09:38PM +0200

Am 13.04.19 um 20:34 schrieb Alf P. Steinbach:
 
> As I recall it was Andrei Alexandrescu who first noted, somewhere in the
> late 200x?, that compilers had progressed to a point where code
> duplication, template code bloat, was no longer a serious issue.
 
I think it is still an issue for C++, it is just no longer that
important. You can still improve cache hit rate by explicit type
erasure. This could be important under some circumstances. The compiler
can only deduplicate identical code. But sometimes it is more efficient
to deduplicate only partial code, e.g. do pointer adjustment in case of
multiple inheritance inline at the caller to keep the core function
identical. I have not seen this as compiler optimization so far.
 
 
Marcel
bitrex <user@example.net>: Apr 13 07:12PM -0400

On 4/13/19 2:34 PM, Alf P. Steinbach wrote:
 
> As I recall it was Andrei Alexandrescu who first noted, somewhere in the
> late 200x?, that compilers had progressed to a point where code
> duplication, template code bloat, was no longer a serious issue.
 
 
The CRTP I like a lot for resource-constrained embedded processors. On
modern compilers it really does seem to allow compile-time interface ->
implementation specialization with no overhead. Tools like Compiler
Explorer show me what code actually generates some asm and what doesn't.
what generally happens in most of my use cases is the appropriate calls
are all figured out and inlined where they need to go, none of the
template-tized interface "stuff" generates anything.
 
 
> One can always dispatch on a class id.
 
> But that's an application level indirection, to avoid a core language
> level indirection, so not likely to be faster.
 
The use case for me is essentially not so much speed (the execution
cycle overhead of a vtable lookup and indirection on a modern RISC
processor is pretty darn low) but, in relatively resource constrained
embedded processors where, because reasons, vtables are copied to SRAM
when they don't strictly need to be. And if I have a lot of small class
instances the extra 4 or 8 bytes for a hidden pointer can become a
significant part of the total object size.
 
But sometimes I do just need to have base pointers stored in a list
where at runtime I don't know what type of derived class the pointer is
pointing to and being able to do a virtual call just makes life so much
easier.
 
> downcasting.
 
> Again there's Andrei: he wrote at length about visitor pattern variants
> in his now classic Modern C++ Design book.
 
I have a copy somewhere around here, it's a pretty dense book so I've
been working my way thru it. Haven't gotten there yet I guess...
 
Paavo Helde <myfirstname@osa.pri.ee>: Apr 13 10:32AM +0300

On 12.04.2019 23:34, Vir Campestris wrote:
> On 12/04/2019 08:15, David Brown wrote:
>> Put "-Wall" in your command line, and gcc will happily warn you.
 
> Not when I tried it. Though I didn't try Gcc8.
 
Interestingly enough, MSVC 2017 refuses to compile your original code.
Probably a bug in MSVC:
 
#include <iostream>
int main() {
bool foo = []() {
std::cout << "various bits of logic\n";
return false;
};
return foo ? 0 : 1;
}
 
1>d:\test\consoletestvs2017\consoletestvs2017\main.cpp(6): error C2440:
'initializing': cannot convert from
'main::<lambda_5ef23b46cf9064836fd2b269f298ffd7>' to 'bool'
1>d:\test\consoletestvs2017\consoletestvs2017\main.cpp(6): note:
Ambiguous user-defined-conversion
Melzzzzz <Melzzzzz@zzzzz.com>: Apr 13 08:06AM

> 'main::<lambda_5ef23b46cf9064836fd2b269f298ffd7>' to 'bool'
> 1>d:\test\consoletestvs2017\consoletestvs2017\main.cpp(6): note:
> Ambiguous user-defined-conversion
 
What user defined conversion?
 
--
press any key to continue or any other to quit...
Paavo Helde <myfirstname@osa.pri.ee>: Apr 13 12:22PM +0300

On 13.04.2019 11:06, Melzzzzz wrote:
>> 1>d:\test\consoletestvs2017\consoletestvs2017\main.cpp(6): note:
>> Ambiguous user-defined-conversion
 
> What user defined conversion?
 
No idea, the example is verbatim.
David Brown <david.brown@hesbynett.no>: Apr 13 01:48PM +0200

On 12/04/2019 22:34, Vir Campestris wrote:
>> Put "-Wall" in your command line, and gcc will happily warn you.
 
> Not when I tried it. Though I didn't try Gcc8.
 
> Andy
 
I tested it with godbolt.org, using gcc "trunk". I haven't tried other
versions, so it could well be a relatively new warning. godbolt.org
will make it easy for you to establish which gcc (or clang, or MSVC)
version you might find useful here.
Manfred <noname@add.invalid>: Apr 13 03:11PM +0200

On 4/13/2019 1:48 PM, David Brown wrote:
> versions, so it could well be a relatively new warning.  godbolt.org
> will make it easy for you to establish which gcc (or clang, or MSVC)
> version you might find useful here.
 
gcc 8.2.1 issues a warning (about the function pointer never being NULL)
with -std=c++17 and -Wall
It will is silent with -std=c++{14,11} regardless of -Wall
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Apr 13 03:54PM +0200

On 13.04.2019 11:22, Paavo Helde wrote:
>     };
>     return foo ? 0 : 1;
> }
 
I believe that Visual C++ 2013 supported a number of raw function
signatures, e.g.
 
auto() -> bool
auto __cdecl () -> bool
auto __stdcall () -> bool
auto __fastcall () -> bool
auto __vectorcall () -> bool
 
... and that it internally provided this conversion via /as if/ type
conversion operator overloads.
 
Assuming those overloads are still in place under the hood, in some
form, that could explain the claimed ambiguity.
 
However, Visual C++2019 appears to give preference/priority to the
undecorated default variant. At least, if you place a `+` in front of
the lambda it compiles instead of choking on internal ambiguity.
 
 
Cheers!,
 
- Alf (hypothesizing mode)
blt_Fh5aD0qv@3sdjijozgu.gov.uk: Apr 13 03:53PM

On Fri, 12 Apr 2019 17:54:25 +0200
>you
>> might as well use as many ingredients as possible.
 
><plink>
 
Plonker.
 
While people like you may think you're impressing everyone by pointlessly
using eevery bit of syntax you can find, most people will think you're simply
a showboating pillock. When you have a swiss army knife you don't have to use
every tool it has just to open a bottle.
wyniijj@gmail.com: Apr 13 03:34AM -0700

How should the idea be implemented to instantiate different definition
of ctors from the template parameter Memcpy_able?
 
//----- file t1.cpp -----
 
#include <iostream>
#include <string.h> // for memcpy
 
template<typename T, bool Memcpy_able>
class Vect {
T m_0, m_1, m_2;
 
public:
 
// Memcpy_able is false (how to enable this definition and the next one
// for different value of Memcpy_able?)
//
// Vect(const T(&arr)[3])
// : m_0(arr[0]), m_1(arr[1]), m_2(arr[2]) {};
 
// Memcpy_able is true
// (Let's assume memcpy can work correctly for the moment)
 
Vect(const T(&arr)[3]) {
::memcpy(&m_0,arr,sizeof(this));
};
};
 
int main()
{
char carr[3]={};
 
Vect<char,true> v(carr);
 
std::cout << sizeof(v) << std::endl;
return 0;
}
//-------------------------
 
Another question is, while trying out the solution, I guessed some rule
of c++ (or g++) might have changed because the value sizeof(Vect<char,true>)
was used to 3*4=12, not 3. Can someone explain this?
 
[me@localhost]$ g++ t1.cpp
[me@localhost]$ ./a.out
3
Bonita Montero <Bonita.Montero@gmail.com>: Apr 13 01:14PM +0200

Classes that have a user-defined constructor aren't pods and
_theoretically_ shouldn't memcpy()'d. But in this case there
arent any complex members so that you won't get into trouble
with any compiler.
Sam <sam@email-scan.com>: Apr 13 10:47AM -0400

> ::memcpy(&m_0,arr,sizeof(this));
> };
> };
 
What I would do is pull out these class members into a different template,
and simply specialize it, then have this `Vect` inherit from it, something
like:
 
template<typename T, bool Memcpy_able> class Vect_base;
 
 
template<typename T>
class Vect_base<T, false> {
 
protected:
T m_0, m_1, m_2;
 
// First constructor
};
 
 
template<typename T>
class Vect_base<T, true> {
 
protected:
T m_0, m_1, m_2;
 
// First constructor
};
 
// If the constructor doesn't need anything else to do, simply inherit it:
 
template<typename T, bool Memcpy_able>
class Vect : Vect_base<T, Memcpy_able> {
 
public:
using Vect_base::Vect_base;
 
 
It's possible that Vect's 2nd template parameter won't be needed at all, and
it can figure out what it must be, for the inherited superclass.
 
 
> [me@localhost]$ g++ t1.cpp
> [me@localhost]$ ./a.out
> 3
 
No rule of C++ has changed. Padding required by classes or class members is
always implementation defined, and can change from compiler to compiler, or
compiler version.
 
You cannot assume that there is, or isn't, padding between class members.
Robert Wessel <robertwessel2@yahoo.com>: Apr 12 06:47PM -0500

Does the standard say anything about the format of the output of
special numeric values, like NaNs and Infs, when formatted by
iostreams?
 
For example:
 
double c=1, d=0;
std::cout << c/d << std::endl;
 
produces: "1.#INF" with MSVC, although I don't think the format is
actually specified.
 
Does the output depend on std::uppercase? IOW, should it be "+Inf" or
"+INF" depending on std::(no)uppercase?
 
And then do I have to worry that someone will be expecting to parse a
possibly implementation dependent format?
 
I'm writing the iostream routines for some numeric routines.
 
Or have I just managed to miss the specification?
Daniel <danielaparker@gmail.com>: Apr 12 08:02PM -0700

> Does the standard say anything about the format of the output of
> special numeric values, like NaNs and Infs, when formatted by
> iostreams?
 
No.
 
> And then do I have to worry that someone will be expecting to parse a
> possibly implementation dependent format?
 
You can detect NaN and infinity and output your own strings, e.g.
 
#include <cmath> // std::isnan, std::isinf
#include <limits> // std::numeric_limits
 
if (std::isnan(x))
{
std::cout << "NaN";
}
else if (x == std::numeric_limits<double>::infinity())
{
std::cout << "+inf";
}
else if (std::isinf(x))
{
std::cout << "-inf";
}
else
{
std::cout << x;
}
 
So they can rely on that.
 
Daniel
https://github.com/danielaparker/jsoncons
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: