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

What is this header good for ?

80 views
Skip to first unread message

Bonita Montero

unread,
Nov 1, 2022, 10:32:16 AM11/1/22
to
#pragma once
#include <type_traits>

template<typename T>
#if defined(__cpp_concpets)
requires std::is_scalar_v<T>
#endif
union ndi_t
{
ndi_t() = default;
ndi_t( T init );
ndi_t &operator =( T assign );
operator T();
T *operator &();
private:
T m_value;
};

template<typename T>
#if defined(__cpp_concpets)
requires std::is_scalar_v<T>
#endif
inline ndi_t<T>::ndi_t( T init ) :
m_value( init )
{
}

template<typename T>
#if defined(__cpp_concpets)
requires std::is_scalar_v<T>
#endif
inline ndi_t<T> & ndi_t<T>::operator =( T assign )
{
m_value = assign;
return *this;
}

template<typename T>
#if defined(__cpp_concpets)
requires std::is_scalar_v<T>
#endif
inline ndi_t<T>::operator T()
{
return m_value;
}

template<typename T>
#if defined(__cpp_concpets)
requires std::is_scalar_v<T>
#endif
inline T *ndi_t<T>::operator &()
{
return &m_value;
}

Alf P. Steinbach

unread,
Nov 1, 2022, 3:24:01 PM11/1/22
to
On 1 Nov 2022 15:32, Bonita Montero wrote:
> #if defined(__cpp_concpets)

That loooks lik a speling eror.

A good-for-nothing header then.

Cheers!,

- Alf

Bonita Montero

unread,
Nov 1, 2022, 10:59:13 PM11/1/22
to
Am 01.11.2022 um 20:23 schrieb Alf P. Steinbach:

> On 1 Nov 2022 15:32, Bonita Montero wrote:

>> #if defined(__cpp_concpets)

> That loooks lik a speling eror.

That's a feature-test macro defined by C++20.

> A good-for-nothing header then.

Thanks for your effort to have a deep understanding of my code. !

Bonita Montero

unread,
Nov 1, 2022, 11:02:58 PM11/1/22
to
That's the final version of my header, now it requires C++20:

#pragma once
#include <type_traits>

template<typename T>
requires std::is_trivially_destructible_v<T>
union ndi_t
{
ndi_t();
ndi_t( T const &init );
ndi_t &operator =( T const &assign );
operator T &();
T *operator &();
T *operator ->()
requires std::is_class_v<T>;
private:
union U
{
U();
U( T const &value );
T m_value;
} m_u;
};

template<typename T>
requires std::is_trivially_destructible_v<T>
inline ndi_t<T>::U::U()
{
}

template<typename T>
requires std::is_trivially_destructible_v<T>
inline ndi_t<T>::U::U( T const &value ) :
m_value( value )
{
}

template<typename T>
requires std::is_trivially_destructible_v<T>
inline ndi_t<T>::ndi_t() :
m_u()
{
}

template<typename T>
requires std::is_trivially_destructible_v<T>
inline ndi_t<T>::ndi_t( T const &init ) :
m_u( init )
{
}

template<typename T>
requires std::is_trivially_destructible_v<T>
inline ndi_t<T> & ndi_t<T>::operator =( T const &assign )
{
m_u.value = assign;
return *this;
}

template<typename T>
requires std::is_trivially_destructible_v<T>
inline ndi_t<T>::operator T &()
{
return m_u.m_value;
}

template<typename T>
requires std::is_trivially_destructible_v<T>
inline T *ndi_t<T>::operator &()
{
return &m_u.m_value;
}


template<typename T>
requires std::is_trivially_destructible_v<T>
inline T *ndi_t<T>::operator ->()
requires std::is_class_v<T>
{
return &m_u.m_value;
}

Tony Oliver

unread,
Nov 2, 2022, 7:45:51 AM11/2/22
to
On Wednesday, 2 November 2022 at 02:59:13 UTC, Bonita Montero wrote:
> Am 01.11.2022 um 20:23 schrieb Alf P. Steinbach:
>
> > On 1 Nov 2022 15:32, Bonita Montero wrote:
>
> >> #if defined(__cpp_concpets)
>
> > That loooks lik a speling eror.
> That's a feature-test macro defined by C++20.

No, it isn't. You seem to be trying (and failing) to spell "__cpp_concepts".
"__cpp_concpets" does not appear anywhere in N4860.

Bonita Montero

unread,
Nov 2, 2022, 9:33:32 AM11/2/22
to
Ok, I removed it anyway, requiring concepts always now because
I'm using a concept which should stop specializations under a
certain condition.

Mut...@dastardlyhq.com

unread,
Nov 2, 2022, 1:00:13 PM11/2/22
to
On Wed, 2 Nov 2022 04:02:53 +0100
Bonita Montero <Bonita....@gmail.com> wrote:
>That's the final version of my header, now it requires C++20:

And this explosion in an ASCII factory does what exactly?

Bonita Montero

unread,
Nov 2, 2022, 1:45:33 PM11/2/22
to
Am 02.11.2022 um 17:59 schrieb Mut...@dastardlyhq.com:

> Bonita Montero <Bonita....@gmail.com> wrote:

>> That's the final version of my header, now it requires C++20:

> And this explosion in an ASCII factory does what exactly?

It's a class that wraps simple types that don't have destructors and
omits their default-construction. With that you can f.e. have a vector
of ints (vector<ndi_t<int>>) and the ints aren't default-constructed,
i.e. their value is arbitrary.
That might save a lot of CPU-time if you grow a large vector in one
step so that you can omit the initialization if you do it yourself
anyway. But there are other standard types like pair<> where the
members are also default-initialized. So if you have a vector of
such pairs where .first and .second are ndi_t-types this also might
save some CPU-time.
There are a number of overloaded operators so that you can handle
such an ndi_t<>-object nearly the same way you would handle the
corresponding type it encapsulates. F.e. you can take the ndi_t<>
object's address with & and you get the address of the encapsulated
type. Or you can access the members of the encapsulated object if
it is a class type with the ->-operator, similar to a smart pointer.

Öö Tiib

unread,
Nov 3, 2022, 11:22:43 AM11/3/22
to
On Wednesday, 2 November 2022 at 19:45:33 UTC+2, Bonita Montero wrote:
> Am 02.11.2022 um 17:59 schrieb Mut...@dastardlyhq.com:
>
> > Bonita Montero <Bonita....@gmail.com> wrote:
>
> >> That's the final version of my header, now it requires C++20:
>
> > And this explosion in an ASCII factory does what exactly?
> It's a class that wraps simple types that don't have destructors and
> omits their default-construction. With that you can f.e. have a vector
> of ints (vector<ndi_t<int>>) and the ints aren't default-constructed,
> i.e. their value is arbitrary.
>
> That might save a lot of CPU-time if you grow a large vector in one
> step so that you can omit the initialization if you do it yourself
> anyway.

How it "might"? IMHO not with what vector is required to do.
We grow by your example a vector like vec.resize(large_size).
That calls void std::vector<T,A>::resize(size_type count, T value = T());
That function is required to copy construct each element that we
did grow using that "value" as argument. On our case that T is
supposed to be ndi_t<Something>. Therefore resize() will use
compiler-generated constructors
ndi_t<Something>::ndi_t( ndit_t const &init ) and those are required
to diligently copy that uninitalized Something value from inside of
that T() object. So what you expect a compiler can "save" here
compared to std::vector<Something> ?

Bonita Montero

unread,
Nov 3, 2022, 11:43:54 AM11/3/22
to
Am 03.11.2022 um 16:22 schrieb Öö Tiib:

> How it "might"? IMHO not with what vector is required to do.
> We grow by your example a vector like vec.resize(large_size).
> That calls void std::vector<T,A>::resize(size_type count, T value = T());
> That function is required to copy construct each element that we
> did grow using that "value" as argument.

The default-constructor of my ndi_t<> is empty so that the loop initia-
lizing the new elements is optimized away. My ndi_t<> is a union so the
member m_value isn't initialized by default but needs explicit initali-
zation with the constructor of my union and I omit that.

Öö Tiib

unread,
Nov 3, 2022, 12:06:56 PM11/3/22
to
What? The resize() is required to copy construct new elements from
"value" parameter, not default construct new elements. What default
constructor of ndi_t does does not matter in context where it is not
called.

Bonita Montero

unread,
Nov 4, 2022, 3:12:23 PM11/4/22
to
Am 03.11.2022 um 17:06 schrieb Öö Tiib:

> What? The resize() is required to copy construct new elements from
> "value" parameter, not default construct new elements. ...

The appended elements are initialized by default if you don't pass
a default-value given to the parametrized constructor. So you can
gigabytes of appended elements that aren't default-initialized and
when you have an operating system that overcommits the appended
memory pages aren't even assigned to physical pages until you
write-touch them.

Öö Tiib

unread,
Nov 5, 2022, 9:24:31 AM11/5/22
to
Plain wrong third time. That is not what resize is required to do. If you
omit the value parameter then it is required to default construct it and
then copy construct the result. Can be billions of times if you resized
to billions of elements.

Bonita Montero

unread,
Nov 5, 2022, 10:51:35 AM11/5/22
to
Am 05.11.2022 um 14:24 schrieb Öö Tiib:

> Plain wrong third time. That is not what resize is required to do.

That depends on what you want. If you want the appended elements not to
be default-initialized because you overwrite the element yourself later
or you don't initialize it at all because you want partitially complete
pages which arent assigned to phyisical memory (unter Linux these pages
arent even reseved from swap with overcommitting) my ndi_t union is the
right thing.
The class only works if the encapsulated type has a trivial destructor
which is restricted by a concept to prevent to do the wrong things with
my union.

Öö Tiib

unread,
Nov 5, 2022, 12:44:11 PM11/5/22
to
On Saturday, 5 November 2022 at 16:51:35 UTC+2, Bonita Montero wrote:
> Am 05.11.2022 um 14:24 schrieb Öö Tiib:
>
> > Plain wrong third time. That is not what resize is required to do.
>
> That depends on what you want.

Nope. If a function is not required by standard to call your mutilated
default constructor then it does not start to call it (and optimize away)
because you want. Your wishes are irrelevant.

Bonita Montero

unread,
Nov 5, 2022, 1:11:47 PM11/5/22
to
Am 05.11.2022 um 17:44 schrieb Öö Tiib:

>> That depends on what you want.

> Nope. If a function is not required by standard to call your mutilated
> default constructor then it does not start to call it (and optimize away)
> because you want. Your wishes are irrelevant.

Of course the default-construction loop on the appended elements
is optimized away, that's what my ndi_t<>-union is for; this saves
CPU time.
You simply don't understand the purpose.


Öö Tiib

unread,
Nov 5, 2022, 7:03:25 PM11/5/22
to
The resize has never been required to have default construction loop
in it; resize is required to have loop of copy initialization.

> You simply don't understand the purpose.

I perfectly understand that you wish that there is default construction
loop in vector resize, just that it is not so in our reality.

Bonita Montero

unread,
Nov 6, 2022, 1:19:55 AM11/6/22
to
Am 06.11.2022 um 00:03 schrieb Öö Tiib:

> The resize has never been required to have default construction loop
> in it; resize is required to have loop of copy initialization.

https://en.cppreference.com/w/cpp/container/vector/resize

"additional default-inserted elements are appended".

> I perfectly understand that you wish that there is default construction
> loop in vector resize, just that it is not so in our reality.

You're a idiot.

Bonita Montero

unread,
Nov 6, 2022, 4:34:35 AM11/6/22
to
I've written a little program to demonstrate the advantage of my ndi_t
union:

#include <iostream>
#include <vector>
#include <chrono>
#include <cmath>
#include <sstream>
#include "ndi_t.h"

using namespace std;
using namespace chrono;

int main()
{
auto bench = []<typename T, typename Touch>( T t, Touch touch ) -> double
requires requires( Touch touch, vector<T> &vt ) { { touch( vt ) }; }
{
constexpr size_t N = (size_t)1 << 30;
vector<T> vt;
auto start = high_resolution_clock::now();
vt.resize( N );
touch( vt );
return (double)duration_cast<nanoseconds>(high_resolution_clock::now()
- start).count() / 1.0e6;
};
auto ndiTouch = []( vector<ndi_t<char>> &ndiVec )
{
for( size_t i = 0; i < ndiVec.size(); ndiVec[i] = 0, i += 0x1000 );
};
double
tChar = bench( char(), []( vector<char> const & ) {} ),
tNdi = bench( ndi_t<char>(), []( vector<ndi_t<char>> const & ) {} ),
tNdiTouch = bench( ndi_t<char>(), ndiTouch );
cout << "char: " << tChar << "ms" << endl;
cout << "ndi_t<char>: " << tNdi << "ms" << endl;
cout << "ndi_t<char> (touch): " << tNdiTouch << "ms" << endl;
}

It benchmarks the time to resize a char-vector to one GB. My ndi_t
union is about 30.000 times faster on my Linux computer. This is
while such large allocations aren't pooled by your memory allocator
but directly fetched from the kernel in multiples of pages. If this
pages aren't touched they remain unassigned to actual physical memory.
0 new messages