Thursday, March 12, 2020

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

jacobnavia <jacob@jacob.remcomp.fr>: Mar 13 12:13AM +0100

Here is one exemple of "structured binding" from the paper of Filipek:
 
int main() {
// Initialize a map table.
const std::map<std::string, int> mapCityPopulation {
{ "Beijing", 21'707'000 },
{ "London", 8'787'892 },
{ "New York", 8'622'698 }
};
 
for (auto&[city, population] : mapCityPopulation)
std::cout << city << ": " << population << '\n';
}
 
Within the for statement the variables "city" and "population" will be
bound automatically to the corresponding fields of the map.
 
Cute isn't it?
 
Before, in the old times, you would have to write:
(I cite the book of Filipek page 29)
 
<quote>
 
Consider a function that returns two results in a pair:
 
std::pair<int, bool> InsertElement(int el) { ... }
 
You can write:
auto ret = InsertElement(...)
And then refer to ret.first or ret.second. However, referring to values
as .first or .second is also not expressive - you can easily confuse the
names, and it's hard to read.
<end quote>
 
Yes, maybe is not "expressive". There is an easy work around though, as
Filipek shows:
 
<quote>
Alternatively you can leverage std::tie which will unpack the tuple/pair
into local variables:
int index { 0 };
bool flag { false };
std::tie(index, flag) = InsertElement(10);
Such code might be useful when you work with std::set::insert which
returns std::pair:std::set<int> mySet;
 
std::set<int>::iterator iter;
bool inserted { false };
 
std::tie(iter, inserted) = mySet.insert(10);
if (inserted)
std::cout << "Value was inserted\n";
 
As you see, such a simple pattern - returning several values from a
function - requires several lines of code.
<end quote>
 
Yes, several lines of code that is easily read by anyone and by existing
tools.
 
And here awe arrive at the essence of why C++ is going into endless
complexity: a desire to have "cute" constructs and save some seconds to
the programmer typing the new code in.
 
Nothing will be said about the need to reprogram all tools that read C++
code to accept the new stuff, the need to figure out all ramifications
of the new construct, etc etc.
 
Because the consequences are terrible when you do not have a 1 to 1
correspondence between the names in the bracket enclosed list, and the
class whose fields will be "bound" to those names.
 
 
Even WORST:
 
What happens when the class is modified to have a new field?
All the structured binding constructs have to be modified to add the new
field, and if you miss one crashes and bugs will inevitably happen.
 
All of this is not necessary using the old syntax that was more verbose
but MUCH SAFER and robust.
 
MINDLESS COMPLXITY FOR COMPLEXITY's SAKE, with no improvement to
anything useful.
 
All the tools have to be rewritten to accomodate this nonsense, what is
not easy since you have to get the definition of the class object that
can be anywhere else, in another header file or whatever!
 
What is the end result?
 
A new useless construct has been added to the language that serves no
purpose other than saving some seconds when typing C++.
 
GREAT!
jacobnavia <jacob@jacob.remcomp.fr>: Mar 12 08:22AM +0100

Le 11/03/2020 à 18:23, Paavo Helde a écrit :
> Curiously, I'm reading this message while looking at the public API of
> the openssl library, which consists of 1,588 free functions. Oh well...
 
Yes, C++ does MUCH MUCH better:
The "POCO" library in C++ is a C++ wrapper of the SSL and features:
 
12 Namespaces
410 Classes
27 Types
56 functions
 
I did not look at all classes but all that I sampled had much more than
10 methods/member functions.
 
Where are the C++ gains here?
 
Oh well... as you say
 
For people foreign to C++ you have to figure out which name space holds
the method you want to call, which class, etc. None of that is present
in C. Just call the function and be done with it.
Daniel <danielaparker@gmail.com>: Mar 12 02:28PM -0700

On Thursday, March 12, 2020 at 3:22:39 AM UTC-4, jacobnavia wrote:
 
> For people foreign to C++ you have to figure out which name space holds
> the method you want to call, which class, etc. None of that is present
> in C. Just call the function and be done with it.
 
But who has time to call all those functions? :-)
 
What we're looking for in modern languages is the ability to work with
abstractions.
 
For example, suppose I receive some CBOR data over the wire in the format
 
0xd8,0x55 -- Tag 85 (float32 little endian Typed Array)
0x5a - byte string (four-byte uint32_t for n, and then n bytes follow)
03 93 87 00 -- 60000000
00 00 00 00 -- 0.0f
00 00 80 3f -- 1.0f
00 00 00 40 -- 2.0f
...
 
which contains a typed array of 60 million floats in little endian format.
 
I want to be able to write simply
 
std::vector<float> y = cbor::decode_cbor<std::vector<float>>(data);
 
without having to write any additional code and irrespective of the details
of the data, for example, its endianness, whether the values are float32
or float16 (half precision float), or whether what's inside the data is a
typed array or a classical CBOR array.
 
Modern languages differ in the support they provide for writers of generic
functions such as decode_cbor. In languages like Java and C# we have
reflection, in languages like C++ and rust, we work with traits. But the goal
is abstraction and expressiveness.
 
Daniel
jacobnavia <jacob@jacob.remcomp.fr>: Mar 12 11:45PM +0100

Le 12/03/2020 à 22:28, Daniel a écrit :
> of the data, for example, its endianness, whether the values are float32
> or float16 (half precision float), or whether what's inside the data is a
> typed array or a classical CBOR array.
 
Sure, C++ is great for that. Just put some hyper general template based
software mountain that will do all details for you.
 
But...
 
Is that really necessary? In practical applications isn't it always the
case that you know what you expect, and how much will it change?
 
There are several implementations of CBOR in C, and they probably need a
few additional calls than your one liner. They do not need a C++ runtime
library and they will fit into a microcontroller.
 
This is a question of an application environment: if you run in a
workstation with plenty of CPU and memory yes, your approach is useful.
If you run in a more constrained environment your approach is just too
big to fit!
 
The C approach is based on a different conception of software than the
C++ approach. Avoiding bloat is not one of C++ strengths really. But it
can be justified in some cases, I will not deny that.
 
This whole discussion would be better served if I expressed my views
with a concrete example, starting a new thread. I will start with
structured bindings and the modification of the if statement introduced
in C++20
Frederick Gotham <cauldwell.thomas@gmail.com>: Mar 12 05:30AM -0700

On Monday, March 9, 2020 at 11:18:20 AM UTC, Frederick Gotham wrote:
 
> to retrieve the first line of text from its stdout. If any kind
> of error takes places, the function will simply return an empty
> string. No exceptions will be thrown.
 
 
Here's my fifth attempt. But first, an explanation:
 
* Each individual command is expected to return its first line of stdout within 2 seconds
* After all commands have returned their first line (or timed out), all outstanding threads are collectively given 8 seconds to finish, and after the 8 seconds, everything gets nuked (safely).
 
I'm still having a little trouble with segfaults inside the function "Handle_Cleanup", but anyway here's my fifth attempt. Oh by the way I realise I went a little crazy with keeping everything inside the function "Run_Command_string", I can of course take some stuff outside the function.
 
ATTEMPT FIVE:
 
#include <boost/process.hpp> /* boost::process::child, ipstream */
#include <boost/chrono.hpp> /* boost::chrono::seconds */
#include <boost/thread.hpp> /* try_join_for */
#include <boost/lockfree/queue.hpp> /* boost::lockfree:queue */
 
#include <string>
 
/* ===== For Testing ===== */
#include <iostream>
using std::cout;
using std::endl;
/* ======================= */
 
/* The next function is to run a command and capture its
first line of stdout. On failure, returns empty string. */
std::string Run_Command_string(std::string const &str_cmd, unsigned const timeout = 2u) noexcept
{
struct Thread_Manager {
 
struct Handle {
boost::process::ipstream *p_ip;
boost::process::child *p_child;
boost::thread *p_thread;
};
 
boost::lockfree::queue<Handle> ctr;
 
Thread_Manager(void) : ctr(600u) {} /* Pre-allocate for 600 elements */

static void Cleanup_Handle(Handle const &h)
{
/* I'm not entirely sure why but I've had
to do some strange stuff in this
function in order to prevent a segfault */

h.p_child->terminate();

delete h.p_thread;
 
h.p_ip->pipe().close();
 
boost::this_thread::sleep_for(boost::chrono::milliseconds(100u)); /* Segfault if you don't do this */
 
delete h.p_ip;

delete h.p_child;
}
 
~Thread_Manager(void)
{
cout << "==============================================\n"
" BEGIN THREAD CLEAN-UP\n"
"==============================================\n";

bool man_alive = false;
 
Handle h;
 
for ( ; ctr.pop(h); Cleanup_Handle(h) )
{
if ( nullptr == h.p_thread )
{
cout << "= = = Alpha Loop: Thread pointer is nullptr = = =\n";
continue;
}
 
if ( false == h.p_thread->joinable() )
{
cout << "= = = Alpha Loop: Thread object is not for an actual thread = = =\n";
continue;
}
 
if ( true == h.p_thread->try_join_for(boost::chrono::milliseconds(1u)) )
{
cout << "= = = Alpha Loop: Thread object is for a finished thread = = =\n";
continue;
}

/* If control reaches here, we have at least one
thread that hasn't finished yet */
 
cout << "= = = Alpha Loop: Thread object is for thread still running = = =\n";

man_alive = true;
break; /* Note that the three pointers haven't been deleted when we break out */
}

if ( false == man_alive )
{
/* Nothing more to do, we can return from the destructor */
cout << "= = = All threads finished. No need for amnesty. = = =\n";
return;
}

/* If control reaches here, there are still some threads running,
and the first one is pointed to by 'p' */

/* Give everything 8 seconds to finish up */

cout << "============================ BEGIN 8 SECOND AMNESTY =====================================\n";
boost::this_thread::sleep_for(boost::chrono::seconds(8u));
cout << "============================ END 8 SECOND AMNESTY =====================================\n";

/* Now just kill any threads still running */
 
goto Label_To_Jump_Into_Loop; /* Because the current handle is still to be deal with */

for ( ; ctr.pop(h); Cleanup_Handle(h) )
{
Label_To_Jump_Into_Loop:

if ( nullptr == h.p_thread )
{
cout << "= = = Beta Loop: Thread pointer is nullptr = = =\n";
continue;
}
 
if ( false == h.p_thread->joinable() )
{
cout << "= = = Beta Loop: Thread object is not for an actual thread = = =\n";
continue;
}
 
if ( true == h.p_thread->try_join_for(boost::chrono::milliseconds(1)) )
{
cout << "= = = Beta Loop: Thread object is for a finished thread = = =\n";
continue;
}

/* If control reaches here, thread that hasn't finished yet */
 
cout << "= = = Beta Loop: Killing thread that didn't stop in time = = =\n";

/* To kill a thread, you call interrupt and immediately follow
it with either "join" or "detach" */

//cout << "From Start\n";
h.p_thread->interrupt();
h.p_thread->detach();
//cout << "To Finish\n";
}

cout << "> = > = > = > = > = > = Destruction of Thread_Manager is finitio > = > = > = > = > = > =\n";

/* All threads are now dead and gone */
return;
}
};

using std::string;
namespace bp = boost::process;
 
try
{
static Thread_Manager mgr;

string retval;
 
Thread_Manager::Handle handle{new bp::ipstream, nullptr, nullptr}; /* First pointer is for capturing stdin from child process */
 
handle.p_child = new bp::child(bp::search_path("sh"), "-c", str_cmd, bp::std_in.close(), bp::std_out > *handle.p_ip, bp::std_err > bp::null);
 
boost::mutex mutex_for_messaging;
boost::atomic_flag have_first_line_yet;
boost::mutex::scoped_lock lock_for_condition(mutex_for_messaging);
boost::condition_variable have_first_line_yet_CONDITION;
 
//cout << "About to start Reader thread\n";

/* Start second thread here to capture the first line of stdout */

handle.p_thread = new boost::thread(
[handle, &retval, &have_first_line_yet, &have_first_line_yet_CONDITION] /* Must take 'handle' by value */
{
try
{
/* Retain the first line of text */
try
{
std::getline(*handle.p_ip,retval);
}
catch (...)
{
retval.clear();
}
 
have_first_line_yet.test_and_set();
have_first_line_yet_CONDITION.notify_one(); /* The main thread is doing 'wait_for' */

/* ==== DANGER DANGER DANGER ==== Run_Command_string might have returned by this point ==== DANGER ==== DANGER */
/* so do not use retval, have_first_line_yet, bHave_first_line_yet past this point */
 
try
{
/* Now just discard the rest of the output */
for (string tmp; std::getline(*handle.p_ip,tmp); ) { /* Do Nothing */ }
}
catch (...)
{
/* Do Nothing */
}
}
catch (...)
{
/* Do Nothing */
}
}
);

mgr.ctr.push(handle);

boost::thread &reader = *handle.p_thread;
 
bool const did_not_timeout = (boost::cv_status::no_timeout == have_first_line_yet_CONDITION.wait_for(lock_for_condition, boost::chrono::seconds(timeout)));
 
/* A few seconds might pass here */
 
if ( did_not_timeout && have_first_line_yet.test_and_set() )
{
/* We got the first line within the timeout! :-) */
 
return retval;
}
else
{
/* Program has frozen, so just return an empty string */
reader.interrupt();
/* reader.detach(); DON'T DO THIS */
handle.p_child->terminate(); /* This might get terminated a second time by Thread_Manager */
}
}
catch (...)
{
/* Do Nothing */
}

return string();
}
 
auto main(void) -> int
{
for (unsigned i = 0; i != 12; ++i)
{
std::string const &retval1 = Run_Command_string("find / | grep bin");

cout << "==== ONE ==== " + retval1 + "\n";

std::string const &retval2 = Run_Command_string("find / | grep usr");
cout << "==== TWO ==== " + retval2 + "\n";

std::string const &retval3 = Run_Command_string("find / | grep lib");
cout << "==== TR3 ==== " + retval3 + "\n";
}
}
felix@palmen-it.de (Felix Palmen): Mar 12 04:00PM +0100

> retrieve the first line of text from its stdout. If any kind of error
> takes places, the function will simply return an empty string. No
> exceptions will be thrown.
 
From looking at the code, this is only for POSIX? Then what's wrong with
popen(), select() and fgets()?
 
Furthermore, what's the problem to be solved here? Often enough, there's
a (much) better way than reading the output of another program.
 
--
Dipl.-Inform. Felix Palmen <felix@palmen-it.de> ,.//..........
{web} http://palmen-it.de {jabber} [see email] ,//palmen-it.de
{pgp public key} http://palmen-it.de/pub.txt // """""""""""
{pgp fingerprint} A891 3D55 5F2E 3A74 3965 B997 3EF2 8B0A BC02 DA2A
Frederick Gotham <cauldwell.thomas@gmail.com>: Mar 12 08:25AM -0700

On Thursday, March 12, 2020 at 3:02:13 PM UTC, Felix Palmen wrote:
 
> From looking at the code, this is only for POSIX? Then what's wrong with
> popen(), select() and fgets()?
 
 
 
Did you assume it's POSIX because I use "ps" and "grep"?
 
 
 
> Furthermore, what's the problem to be solved here? Often enough, there's
> a (much) better way than reading the output of another program.
 
 
 
In my work there's a guy who has written Linux shell script files that are hundreds of lines long. I think Linux shell scripting is fine, I use it to write about 20 or 30 lines, but for anything more complicated I'll do it in C or C++.
 
Anyway... I'm sort of half-porting one of his insanely long scripts to C++, and there's loads of places where he takes the output from another program. In the future I might look at cleaner ways of achieving my objectives, but for right now I'm just gonna run these other programs and collect their first line of output.
 
In the sample code in my previous post, I was just using "find / | grep bin" as an example of a command that gives its first line quite quickly but can then take minutes to spit out all the rest of its lines.
 
I'm working on embedded Linux right now (arm 32-bit) but I'd like to be able to write this with Boost so that it works on lots of systems.
 
I'm not entirely sure why I'm getting segfaults in "Cleanup_Handle".
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Mar 12 10:47AM -0700

>> From looking at the code, this is only for POSIX? Then what's wrong with
>> popen(), select() and fgets()?
 
> Did you assume it's POSIX because I use "ps" and "grep"?
 
Or you could have just answered the question. Is it only for POSIX?
 
[...]
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */
cdalten@gmail.com: Mar 12 12:06PM -0700

On Thursday, March 12, 2020 at 5:30:50 AM UTC-7, Frederick Gotham wrote:
> cout << "==== TR3 ==== " + retval3 + "\n";
> }
> }
 
Give it up. Let it go. Move on.
Frederick Gotham <cauldwell.thomas@gmail.com>: Mar 12 01:41AM -0700

According to the C++11 Standard, if you create an object of type "std::thread" and start it running, then you must either 'join' or 'detach' before the destructor of the "std::thread" object is called, otherwise "std::terminate" gets called.
 
Are there any pitfalls to doing the following?
 
class Safe_Thread : public std::thread {
public:
 
~Safe_Thread(void)
{
std::thread::detach();
}
};
 
My aim here is to have a thread class that behaves exactly like "std::thread" (or indeed boost::thread) but which doesn't call std::terminate if you don't 'join' or 'detach'.
David Brown <david.brown@hesbynett.no>: Mar 12 10:45AM +0100

On 12/03/2020 09:41, Frederick Gotham wrote:
> }
> };
 
> My aim here is to have a thread class that behaves exactly like "std::thread" (or indeed boost::thread) but which doesn't call std::terminate if you don't 'join' or 'detach'.
 
Yes, there are pitfalls. First, it's going to be bad for a thread
object that does not own a thread (such as after you've called detach,
or after you've moved the object, or after you've joined the thread).
Secondly, you are left with these threads hanging around that you can't
access and tidy up. Third, if you have started a thread, you have some
idea of what you want to do with it - such as join it or detach it.
Paavo Helde <myfirstname@osa.pri.ee>: Mar 12 12:12PM +0200

On 12.03.2020 10:41, Frederick Gotham wrote:
> }
> };
 
> My aim here is to have a thread class that behaves exactly like "std::thread" (or indeed boost::thread) but which doesn't call std::terminate if you don't 'join' or 'detach'.
 
Your aim is understandable, but not very wise. A detached thread cannot
be joined, meaning that your program is basically not able to guarantee
a clean shutdown any more. Failing to join the threads properly is just
sloppy programming.
 
If you still insist on having detached threads, I suggest to use _exit()
for terminating the program, this will kill it on spot without any
cleanup of global statics etc, and so it should avoid any errors in the
still running threads. A decent OS will clean up the system resources
automatically when the process dies, so you might come off relatively
safe with this (unless the threads perform some externally visible tasks
like writing files, of course).
cdalten@gmail.com: Mar 12 05:01AM -0700

On Thursday, March 12, 2020 at 1:41:36 AM UTC-7, Frederick Gotham wrote:
> }
> };
 
> My aim here is to have a thread class that behaves exactly like "std::thread" (or indeed boost::thread) but which doesn't call std::terminate if you don't 'join' or 'detach'.
 
So like my girlfriend dragged me to see Debbie Gibson play in San Jose. I was kind of shocked that the woman didn't have a big butt. This is because, like, a lot of women, past a certain age, like, tend to get a big ass.
cdalten@gmail.com: Mar 12 05:03AM -0700

On Thursday, March 12, 2020 at 3:12:13 AM UTC-7, Paavo Helde wrote:
> automatically when the process dies, so you might come off relatively
> safe with this (unless the threads perform some externally visible tasks
> like writing files, of course).
 
Going off on a tangent, when possible, I tend to use function callbacks instead of threads. I also tend to drink Mt. Dew instead of Diet Coke.
"Öö Tiib" <ootiib@hot.ee>: Mar 12 07:35AM -0700

On Thursday, 12 March 2020 10:41:36 UTC+2, Frederick Gotham wrote:
> }
> };
 
> My aim here is to have a thread class that behaves exactly like "std::thread" (or indeed boost::thread) but which doesn't call std::terminate if you don't 'join' or 'detach'.
 
Do whatever you want. Lousy management of most precious
resources like peripheral devices, processor cores, processes,
i/o sockets, file descriptors or threads of execution are symptoms
of usual trashware (that is over 95% of all programs).
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: