Saturday, December 15, 2018

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

"Chris M. Thomasson" <invalid_chris_thomasson@invalid.invalid>: Dec 14 10:25PM -0800

Fwiw, there is an interesting difference between GCC and MSVC. MSVC
automatically calls the thread local constructors. Take this program
into account:
__________________
 
#include <iostream>
#include <thread>
#include <atomic>
#include <mutex>
#include <cassert>
 
 
#define THREADS 4
 
 
#define CT_MB_ACQ std::memory_order_acquire
#define CT_MB_REL std::memory_order_release
#define CT_MB_RLX std::memory_order_relaxed
 
 
static std::atomic<unsigned long> g_per_thread_ctor(0);
static std::atomic<unsigned long> g_per_thread_dtor(0);
static std::mutex g_cout_mutex;
 
 
struct ct_per_thread
{
unsigned long m_id;
 
ct_per_thread()
: m_id(g_per_thread_ctor.fetch_add(1, CT_MB_RLX))
{
 
}
 
~ct_per_thread()
{
g_per_thread_dtor.fetch_add(1, CT_MB_RLX);
}
};
 
 
static thread_local ct_per_thread g_per_thread;
 
 
void ct_worker()
{
ct_per_thread& self = g_per_thread;
 
g_cout_mutex.lock();
std::cout << "ct_worker::(" << self.m_id << ")\n";
g_cout_mutex.unlock();
}
 
 
int main()
{
ct_per_thread& self = g_per_thread;
 
std::cout << "main::(" << self.m_id << ")\n";
 
{
std::thread threads[THREADS];
 
for (unsigned long i = 0; i < THREADS; ++i)
{
threads[i] = std::thread(ct_worker);
}
 
for (unsigned long i = 0; i < THREADS; ++i)
{
threads[i].join();
}
}
 
unsigned long a = g_per_thread_ctor.load(CT_MB_RLX);
unsigned long b = g_per_thread_dtor.load(CT_MB_RLX) + 1;
 
std::cout << "a = " << a << "\n";
std::cout << "b = " << b << "\n";
 
assert(a == b);
 
std::cout << "\n\nmain() - exit\n";
std::cout.flush();
 
return 0;
}
__________________
 
 
This should product output like, where a is always equal to b:
__________________
ct_worker::(1)
ct_worker::(2)
ct_worker::(3)
ct_worker::(4)
a = 5
b = 5
__________________
 
 
However, on GCC, when one omits the first two lines of code in main:
__________________
ct_per_thread& self = g_per_thread;
 
std::cout << "main::(" << self.m_id << ")\n";
__________________
 
 
GCC does not call the ctor for self, while MSVC does. Try getting rid of
those two lines of code and run it under GCC. It should go something like:
__________________
ct_worker::(0)
ct_worker::(1)
ct_worker::(2)
ct_worker::(3)
a = 4
b = 5
__________________
 
Where a does not equal b. Humm...
 
Should the ctor for a thread local be automatically called on thread
creation, even main, or... Should it be delayed until a thread actually
uses it? Need to dig into the standard.
"Chris M. Thomasson" <invalid_chris_thomasson@invalid.invalid>: Dec 14 11:01PM -0800

On 12/14/2018 10:25 PM, Chris M. Thomasson wrote:
>         g_per_thread_dtor.fetch_add(1, CT_MB_RLX);
>     }
> };
[...]
 
Can a thread local be a non-POD type?
Paavo Helde <myfirstname@osa.pri.ee>: Dec 15 05:06PM +0200

On 15.12.2018 8:25, Chris M. Thomasson wrote:
 
> Should the ctor for a thread local be automatically called on thread
> creation, even main, or... Should it be delayed until a thread actually
> uses it? Need to dig into the standard.
 
The standard says a thread local "shall be initialized before its first
odr-use". I guess this is for supporting the "zero overhead" principle:
no resources should be wasted for construction of a thread local in the
threads which don't use it.
"Chris M. Thomasson" <invalid_chris_thomasson@invalid.invalid>: Dec 15 11:48AM -0800

On 12/15/2018 7:06 AM, Paavo Helde wrote:
> odr-use". I guess this is for supporting the "zero overhead" principle:
> no resources should be wasted for construction of a thread local in the
> threads which don't use it.
 
Thanks a million for it really helps clear things up. The funny part is
that MSVC still calls the constructor for the thread local in main, even
if we delete the lines of code that actually use it. For instance:
________________________
int main()
{
//ct_per_thread& self = g_per_thread;
 
//std::cout << "main::(" << self.m_id << ")\n";
 
{
std::thread threads[THREADS];
 
for (unsigned long i = 0; i < THREADS; ++i)
{
threads[i] = std::thread(ct_worker);
}
 
for (unsigned long i = 0; i < THREADS; ++i)
{
threads[i].join();
}
}
 
unsigned long a = g_per_thread_ctor.load(CT_MB_RLX);
unsigned long b = g_per_thread_dtor.load(CT_MB_RLX) + 1;
 
std::cout << "a = " << a << "\n";
std::cout << "b = " << b << "\n";
 
assert(a == b);
 
std::cout << "\n\nmain() - exit\n";
std::cout.flush();
 
return 0;
}
________________________
 
 
MSVC outputs:
________________________
ct_worker::(1)
ct_worker::(2)
ct_worker::(3)
ct_worker::(4)
a = 5
b = 5
 
 
main() - exit
________________________
 
 
 
GCC outputs:
________________________
ct_worker::(1)
ct_worker::(2)
ct_worker::(0)
ct_worker::(3)
a = 4
b = 5
Assertion failed!
________________________
 
 
GCC is giving the correct output according to the standard. MSVC
constructs a ct_per_thread in main no matter what. Humm...
James Kuyper <jameskuyper@alumni.caltech.edu>: Dec 15 03:37PM -0500

On 12/15/18 02:01, Chris M. Thomasson wrote:
...
> Can a thread local be a non-POD type?
 
The only restrictions on the use of thread_local are it "shall be
applied only to the names of variables of namespace or block scope and
to the names of static data members." (7.1.1p3). Whether or not it's a
POD type has no relevance.
Paavo Helde <myfirstname@osa.pri.ee>: Dec 16 12:05AM +0200

On 15.12.2018 21:48, Chris M. Thomasson wrote:
 
> Thanks a million for it really helps clear things up. The funny part is
> that MSVC still calls the constructor for the thread local in main, even
> if we delete the lines of code that actually use it.
 
Everything is "before" an event which never happens, so MSVC is arguably
correct here from a legal viewpoint.
"Chris M. Thomasson" <invalid_chris_thomasson@invalid.invalid>: Dec 14 07:22PM -0800

On 12/13/2018 4:13 PM, Chris Vine wrote:
> auto callback = [](int a, int b) {std::cout << a + b << std::endl;};
> call_except_last(callback, 5, 6, 7);
> }
 
Nice. I can use this for other things.
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Dec 15 11:25AM

On Fri, 14 Dec 2018 11:42:27 -0800 (PST)
 
> // here is the problem, I can know that one argument must be omitted, but
> // there is no way to remove it with the parameter pack
 
> }
 
I still don't think that I have grasped your problem. If you are
writing your own implementation of a signal/slot framework, when
invoking the receiving object's member function then, from the arguments
supplied by the emitter, only pass it the number of arguments which in
fact represents the receiving function's arity.
 
You (the programmer invoking 'connect') must know the arity of the
receiving member function that you are "connecting" in your code and
which the signal will dispatch. This arity which you must know could be
passed in as a function parameter of 'connect', or you could derive it
programmatically: see an example below - you would need overloads of
the get_arity() function in order to cover const and non-const member
functions, but that is a task for you. Since the get_arity() function
is constexpr, you could use it together with the tuple and index list
approach, but that seems like overkill: instead you could drop the
surplus arguments as a runtime operation when invoking the receiving
function.
 
Presumably your connect function is a template function so there is
lots more work for you to do if you are writing your own generic
signal/slot framework.
 
**********************
 
#include <cstddef>
#include <iostream>
 
void my_func2(int a, int b) {}
void my_func3(int a, int b, int c) {}
 
template <class Ret, class... Args>
constexpr std::size_t get_arity(Ret(*)(Args...)) {
return sizeof...(Args);
}
 
int main () {
std::cout << get_arity(my_func2) << std::endl;
std::cout << get_arity(my_func3) << std::endl;
}
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Dec 15 12:44PM

On Sat, 15 Dec 2018 11:25:47 +0000
> approach, but that seems like overkill: instead you could drop the
> surplus arguments as a runtime operation when invoking the receiving
> function.
 
By the way, this get_arity() function will not work with lambda
expressions. But that is fine - a lambda expression is a function
literal supplied by the programmer on the fly. You can document that if
passing a lambda expression to your connect() function, then the
signature of the lambda expression must match the signal's signature
exactly, even if some arguments are unused in the lambda.
 
You can detect lambdas by having a templated catch-all of 'get_arity'
for cases where there is no better match, which returns say size_t(-1)
to indicate that the arity cannot be deduced. In such a case, you
can pass on all the emitter's arguments to the lambda.
 
// where there is no better match
template <class Func>
constexpr std::size_t get_arity(Func&&) {
return (std::size_t) -1;
}
Christian Gollwitzer <auriocus@gmx.de>: Dec 15 10:43PM +0100

Am 14.12.18 um 20:42 schrieb User:
> void clicked(int x, bool status, int other);
> };
> [...]
 
Have you checked how this was solved by other frameworks? Qt uses a
special compiler and runtime string comparison. But there is another
library called libsigc++ with a similar functionality based on templates:
 
https://github.com/libsigcplusplus/libsigcplusplus
 
In a previous versions, the number of arguments was limited, because
they'd written out the templates for up to 10 arguments. I don't know if
that was changed with the advent of variadic templates.
 
Is there a good reason to implement your own framework instead of just
using libsigc++?
 
Christian
Juha Nieminen <nospam@thanks.invalid>: Dec 15 08:25AM

> I'd make the C array an array of pointers to objects.
 
If you can have an array of objects instead of an array of pointers
to objects, why would you deliberately choose the latter? The former
is much easier to use, and usually much more efficient (especially
if in the latter case you are allocating each object dynamically and
individually). The former is also safer (and easier to use safely)
than the latter.
 
The main situation where you would want to use an array of pointers
to (dynamically allocated) objects is if those objects are of
different types. (In some situations you might also want to do that
if what you actually need is an array of "references". Since you
can't actually use references in an array, you'll have to use
pointers instead.)
 
> avoided. I prefer to think that if you don't understand
> how to use them properly, you shouldn't be programming in
> a language that supports them.
 
If you can easily and fluently avoid using a pointer, in general
it's a good idea to do so. Your code usually becomes simpler, cleaner
and safer. This doesn't mean one isn't very experienced in using
pointers.
JiiPee <no@notvalid.com>: Dec 15 10:25AM

On 15/12/2018 08:25, Juha Nieminen wrote:
>> I'd make the C array an array of pointers to objects.
> If you can have an array of objects instead of an array of pointers
> to objects, why would you deliberately choose the latter?
 
I used to do the pointer array when I was a beginner because "it looked
cool", hehe. But now I prefer objects.
Although storing unique pointers is also very safe nowadays, but no
reason really to do it other than what you said (polymorphism).
 
> it's a good idea to do so. Your code usually becomes simpler, cleaner
> and safer. This doesn't mean one isn't very experienced in using
> pointers.
 
yes, I think safety always first even if experienced. It just reduces
risk, and reducing risk is good for experienced as well
scott@slp53.sl.home (Scott Lurndal): Dec 15 03:36PM

>> I'd make the C array an array of pointers to objects.
 
>If you can have an array of objects instead of an array of pointers
>to objects, why would you deliberately choose the latter?
 
Cache management.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Dec 15 06:34PM +0100

On 15.12.2018 09:25, Juha Nieminen wrote:
>> I'd make the C array an array of pointers to objects.
 
> If you can have an array of objects instead of an array of pointers
> to objects, why would you deliberately choose the latter?
 
Generally, not considering the context of the question, the main example
is a jagged array.
 
Then there is the array of items of different most derived types.
 
And the case where the pointers /are/ your objects to be stored.
 
But in context, Scott was most probably giving one way of postponing
initialization until actual use.
 
Another way to do that is like `std::vector`, with in-place construction
in a raw buffer.
 
 
Cheers!,
 
- Alf
James Kuyper <jameskuyper@alumni.caltech.edu>: Dec 15 03:31PM -0500

On 12/15/18 03:25, Juha Nieminen wrote:
>> I'd make the C array an array of pointers to objects.
 
> If you can have an array of objects instead of an array of pointers
> to objects, why would you deliberately choose the latter?
 
Because the objects are much bigger than the pointers, and the array
frequently needs to be rearranged.
Nikki Locke <nikki@trumphurst.com>: Dec 14 11:23PM

Available C++ Libraries FAQ
 
URL: http://www.trumphurst.com/cpplibs/
 
This is a searchable list of libraries and utilities (both free
and commercial) available to C++ programmers.
 
If you know of a library which is not in the list, why not fill
in the form at http://www.trumphurst.com/cpplibs/cppsub.php
 
Maintainer: Nikki Locke - if you wish to contact me, please use the form on the website.
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: