Saturday, March 28, 2020

Digest for comp.lang.c++@googlegroups.com - 21 updates in 3 topics

"Öö Tiib" <ootiib@hot.ee>: Mar 27 06:20PM -0700

On Friday, 27 March 2020 23:08:32 UTC+2, James Kuyper wrote:
 
> (n4659.pdf 10.1.5p5)
 
> Given the above declarations, what would you prefer for
> noexcept(f(true)) to return?
 
false
David Brown <david.brown@hesbynett.no>: Mar 28 04:44PM +0100

On 27/03/2020 19:52, Öö Tiib wrote:
>> low-level and embedded work exceptions are disabled.)
 
> So the constant expression foo(42) can throw since it is somehow
> evaluated run-time? May be ... I don't know how.
 
That's not what I said. I said that "foo()" might be evaluated at
run-time. Perhaps foo(43) would throw - and then "foo(43)" is not a
constant expression. My understanding is that "noexcept(..)" should not
evaluate its operand (like sizeof() and other such operators). It
merely requires the type of the parameters, and their values don't
matter. So if "foo(43)" might throw, then as far as noexcept() is
concerned, "foo(x)" could throw regardless of which "x" you use.
 
Having said that, it surprises me a little that the compiler can't use
its knowledge of foo (whose definition must be visible) to see that
"foo(x)" never throws for any x, and thus consider it as though it were
marked "noexcept" if it knows there are no exceptions ever thrown.
 
My guess for this is one of complexity and consistency. In general, it
would be impossible for a compiler to be see that a non-throwing
function really cannot throw - it would be solving the halting problem.
In practice, it would be easy to see for some functions and hard for
other functions. This would mean that some compilers would be able to
see that a function is non-throwing, while others would not do enough
analysis and thus treat it as "might throw". This would be a very
unpleasant inconsistency.
 
The only clear, consistent and understandable rule would be to say that
constexpr functions are like other functions - they must be considered
as potentially throwing unless explicitly marked nonexcept by the
programmer.
 
(This is based on my understanding and reasoning, rather than the
standards - frankly, I haven't managed to get my head around the
standards here as yet.)
 
> Reasons why
> exceptions are often disabled in embedded world are unrelated and
> even the situation of these being disabled is actually non-conforming.
 
That is correct. I mentioned it merely to show the silliness of your
idea that the committee members are deliberately sabotaging embedded and
low-level programming in C++.
 
 
 
> If you can write with straight face that compile time evaluated expression
> may throw is normal then also you will write with same straight face that
> my code is garbage. So lets better drop that.
 
Currently, I don't think I would consider your code (that requires
volatile usage that will be deprecated in C++20) to be garbage. I
consider it imaginary. (I believe that this is due to your
misunderstandings about what the changes to volatile in C++20 mean, not
because I think you are being deliberately awkward.)
James Kuyper <jameskuyper@alumni.caltech.edu>: Mar 28 01:22PM -0400

On 3/28/20 11:44 AM, David Brown wrote:
> On 27/03/2020 19:52, Öö Tiib wrote:
...
 
> (This is based on my understanding and reasoning, rather than the
> standards - frankly, I haven't managed to get my head around the
> standards here as yet.)
 
"The predicate indicating whether a function cannot exit via an
exception is called the exception specification of the function. If the
predicate is false, the function has a potentially-throwing exception
specification, otherwise it has a non-throwing exception specification.
The exception specification is either defined implicitly, or defined
explicitly by using a noexcept-specifier as a suffix of a function
declarator (11.3.5).
noexcept-specifier:
noexcept ( constant-expression )
noexcept
throw ( )"
(18.4p1).
 
Note that the phrases "exception specification", "potentially-throwing
exception specification" and "non-throwing exception specification" are
all italicized, an ISO convention indicating that this clause
constitutes the official definition of those terms.
 
Therefore, the value of a noexcept() expression is determined entirely
by the exception specification of the function. The body of the function
plays no role in determining that value, not even if it's declared
inline in the same translation unit.
...
> consider it imaginary. (I believe that this is due to your
> misunderstandings about what the changes to volatile in C++20 mean, not
> because I think you are being deliberately awkward.)
 
That's why we both keep asking him to provide examples - so we can say
to him "such code will not be deprecated in C++2020".
"Öö Tiib" <ootiib@hot.ee>: Mar 28 10:42AM -0700

On Saturday, 28 March 2020 17:45:09 UTC+2, David Brown wrote:
> see that a function is non-throwing, while others would not do enough
> analysis and thus treat it as "might throw". This would be a very
> unpleasant inconsistency.
 
I already did cite relevant parts of C++14 where it was required and
C++17 where it was removed. These were 8 words "unless the call is a
constant expression". So if foo(43) is not constant expression but
foo(42) is then noexcept(foo(43)) should be false and noexcept(foo(42))
true. I do not see what about it was inconsistent. I see how it was
great. I could compile time decide between calling non-constexpr
algorithms (library functions, compiler intrinsics, inline assembler
or some function that did cache something) and some potentially less
efficient runtime but constexpr algorithms in C++14.
And I am 100% sure that all compilers do that constant expression
analysis just telling the results to programmer were suddenly
forbidden by C++17.
 
> constexpr functions are like other functions - they must be considered
> as potentially throwing unless explicitly marked nonexcept by the
> programmer.
 
Your immediate reaction was different: "But how could a constant
expression be potentially throwing?". Now you are advocating that
it is most consistent for it to be potentially throwing.
 
 
> That is correct. I mentioned it merely to show the silliness of your
> idea that the committee members are deliberately sabotaging embedded and
> low-level programming in C++.
 
Not only embedded programming on case of constexpr. Whole tool of detecting
constant expressions in whatever context was taken away. Programmer has
to make that analysis manually. I did show that it was made even in
C++11 and C++14 (that had the requirement) modes of compilers.
So fine, I am just stupid and silly when I complain that our tools were
clearly and deliberately damaged. Damaged with unprecedented retroactive
effect in C++11 and C++14 modes as well.
 
> consider it imaginary. (I believe that this is due to your
> misunderstandings about what the changes to volatile in C++20 mean, not
> because I think you are being deliberately awkward.)
 
The change deprecates volatile qualified member functions and so
possibility to detect volatile this with overload resolution.
You have technically already rejected usefulness of it with your
praise of changes and don't see value in it.
 
I am convinced in it because you do not see value in tool of detecting
constexpr with explicit code but instead call it inconsistent. Yes,
constant expressions weren't made detectable with some
std::is_constexpr(foo(42)) but it wasn't needed as there was possible
to write it using noexcept.
 
On any case both possibilities that I am just stupid and silly
in wanting my code to work or that there is sabotage of my tools
make me equally unhappy.
David Brown <david.brown@hesbynett.no>: Mar 28 08:35PM +0100

On 28/03/2020 18:42, Öö Tiib wrote:
> So fine, I am just stupid and silly when I complain that our tools were
> clearly and deliberately damaged. Damaged with unprecedented retroactive
> effect in C++11 and C++14 modes as well.
 
noexcept() has never been about "detecting constant expressions"!
 
Until C++20, there was no way of detecting constant expressions. Now
there is "is_constant_evaluated", constinit and consteval. To my mind,
C++20 has significantly improved the scope for constant expressions and
compile-time programming.
 
> possibility to detect volatile this with overload resolution.
> You have technically already rejected usefulness of it with your
> praise of changes and don't see value in it.
 
I have asked you repeatedly to show an example that /you/ think is good
code design, and which relies on a feature of volatile that is (or which
you think is) deprecated in C++20. I have not asked you to show code
you think /I/ will like - that would be entirely unreasonable.
 
> constant expressions weren't made detectable with some
> std::is_constexpr(foo(42)) but it wasn't needed as there was possible
> to write it using noexcept.
 
You are conflating three completely separate concepts here. noexcept()
has nothing to do with detecting constant expressions. And neither of
these is remotely connected with volatile or the changes to volatile in
C++20.
 
I am still unsure as to whether you have misunderstood something in
C++20 (or perhaps in an earlier version), or if you have a point which
you are failing to demonstrate. Until we get an example, no one can tell.
 
James Kuyper <jameskuyper@alumni.caltech.edu>: Mar 28 03:38PM -0400

On 3/28/20 1:42 PM, Öö Tiib wrote:
> On Saturday, 28 March 2020 17:45:09 UTC+2, David Brown wrote:
...
>> misunderstandings about what the changes to volatile in C++20 mean, not
>> because I think you are being deliberately awkward.)
 
> The change deprecates volatile qualified member functions and so
 
P1152R0 mentions deprecating volatile-qualified member functions. The
notes concerning the changes between r0 and r1 list the results of votes
on several different issues involving volatile-qualified member
functions, all but one of which was approved. However, in r4, I can find
no other mention of deprecating them. Can you point me to the clause (in
P1152R4) where that occurs?
"Öö Tiib" <ootiib@hot.ee>: Mar 28 12:42PM -0700

On Saturday, 28 March 2020 21:35:23 UTC+2, David Brown wrote:
 
> noexcept() has never been about "detecting constant expressions"!
 
I already did cite relevant parts of C++14 where it was required and
C++17 where it was removed. These were 8 words "unless the call is a
constant expression". So if foo(43) is not constant expression but
foo(42) is then noexcept(foo(43)) should be false and noexcept(foo(42))
true.
James Kuyper <jameskuyper@alumni.caltech.edu>: Mar 28 03:54PM -0400

On 3/28/20 3:42 PM, Öö Tiib wrote:
> constant expression". So if foo(43) is not constant expression but
> foo(42) is then noexcept(foo(43)) should be false and noexcept(foo(42))
> true.
 
A call to a constexpr function whose definition has been provided, has
defined behavior, and which meets the requirements for a constexpr
function (10.1.5) doesn't match any of the items in the list under
8.20p2 - as such, it can never fail to qualify as a constant expression,
regardless of which value is passed to the function.
"Öö Tiib" <ootiib@hot.ee>: Mar 28 01:16PM -0700

On Saturday, 28 March 2020 21:35:23 UTC+2, David Brown wrote:
> there is "is_constant_evaluated", constinit and consteval. To my mind,
> C++20 has significantly improved the scope for constant expressions and
> compile-time programming.
 
It does not work and so you are under some kind of illusion from
worm-tongue-wording of standard.
 
#include <iostream>
#include <type_traits>
 
constexpr int foo(int x)
{
if (std::is_constant_evaluated())
return x;
return 666;
}
 
int main()
{
std::cout << (foo(42) == 42 ? "constant expressions can be detected\n"
: "constant expressions can't be "
"detected\n");
}
 
Demo: <http://coliru.stacked-crooked.com/a/61f3d3e62656525e>
"Öö Tiib" <ootiib@hot.ee>: Mar 28 02:12PM -0700

On Saturday, 28 March 2020 21:54:54 UTC+2, James Kuyper wrote:
> function (10.1.5) doesn't match any of the items in the list under
> 8.20p2 - as such, it can never fail to qualify as a constant expression,
> regardless of which value is passed to the function.
 
Is it removed that when conditional expression evaluates into throw
expression then it is not constant expression?
James Kuyper <jameskuyper@alumni.caltech.edu>: Mar 28 06:25PM -0400

On 3/28/20 5:12 PM, Öö Tiib wrote:
>> regardless of which value is passed to the function.
 
> Is it removed that when conditional expression evaluates into throw
> expression then it is not constant expression?
In n3337.pdf dated 2012-01-16, section 5.19p2 includes "an invocation of
a constexpr function with arguments that, when substituted by function
invocation substitution (7.1.5), do not produce a constant expression;"
as one of the things that fails to qualify as a constant expression.
 
n3797.pdf, dated 2013-10-13, drops that item from the list.
 
So it would appear that the change you're complaining about took place
about 7-8 years ago.
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Mar 28 04:11PM -0700


> n3797.pdf, dated 2013-10-13, drops that item from the list.
 
> So it would appear that the change you're complaining about took place
> about 7-8 years ago.
 
Drafts of the C++ standard are available in this git repo:
 
https://github.com/cplusplus/draft
 
The commit that removed that wording is:
 
commit 7cb8947bec0eaf7788e75e3b36e23f4714e157a8
Author: Richard Smith <richard@metafoo.co.uk>
Date: 2012-05-05 19:59:36 -0700
 
N3652 Relaxing constraints on constexpr functions

Conflicts resolved as indicated by N3652.
 
N3652 is here: https://isocpp.org/files/papers/N3652.html
 
Relaxing constraints on constexpr functions
 
constexpr member functions and implicit const
 
This paper describes the subset of N3597 selected for inclusion in
C++14, relaxing a number of restrictions on constexpr
functions. These changes all received overwhelmingly strong or
unopposed support under review of the Evolution Working Group. It
also incorporates Option 2 of N3598.
 
--
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 */
red floyd <no.spam@its.invalid>: Mar 28 11:04AM -0700

I was looking for a way to use standard algorithms with a range of
integers. For example, for the range [1, N), where N can be arbitrarily
large. The problem to me appears to be that library algorithms only
work on iterators. I didn't want to just create a collection containing
the integers, because I was playing with some math which might use large
values (on the order of over a million), and I didn't want to waste the
space.
 
So for my purposes, I came up with the following.
 
[BEGIN CODE]
namespace my {
template<typename Integral, typename UnaryFunction>
Integral for_each(Integral first, Integral last, UnaryFunction func)
{
while (first < last)
{
func(first);
++first;
}
return first;
}
template<typename Integral, typename UnaryFunction>
Integral for_each(Integral first, Integral last,
Integral interval, UnaryFunction func)
{
while (first < last)
{
func(first);
first += interval;
}
return first;
}
}
[END CODE]
 
I don't want to duplicate something already written, is there
something similar in the Standard Library?
 
-- red floyd
Sam <sam@email-scan.com>: Mar 28 02:43PM -0400

red floyd writes:
 
> the integers, because I was playing with some math which might use large
> values (on the order of over a million), and I didn't want to waste the
> space.
 
It is trivial to whip up an iterator that will iterate over a sequence of a
billion values, that will require zero bytes of memory. The size of the
sequence covered by an iterator has no direct relation to the amount of
memory required for it. There are iterators that iterate over something
that's not represented by a memory range.
 
People's Exhibit "A": you might be familiar with std::istreambuf_iterator?
It will happily iterate over each byte of a terabyte-sized file, without
needing a terabyte of RAM.
 
You need to divorce yourself from the notion that just because you have an
iterator that produces a million ints, these million int must be in RAM,
somewhere.
 
> [END CODE]
 
> I don't want to duplicate something already written, is there
> something similar in the Standard Library?
 
Well, you can use std::for_each, with a custom iterator whose operator*
return *this, perhaps.
 
Or, maybe std::fill or std::generate can be put to good use here, with a few
custom classes.
Bonita Montero <Bonita.Montero@gmail.com>: Mar 28 08:30PM +0100

You could use an iterator which wraps a normal iterator and includes
a gap-value. Something like this (don't know if everything is correct):
 
#include <iterator>
 
template<typename RandIt>
struct gap_iterator
{
using value_type = typename std::iterator_traits<RandIt>::value_type;
gap_iterator() = default;
gap_iterator( RandIt it, std::size_t gap );
gap_iterator( gap_iterator const &other );
gap_iterator &operator =( gap_iterator const &rhs );
bool operator !=( gap_iterator const &other );
value_type &operator *();
value_type *operator ->();
gap_iterator &operator ++();
gap_iterator &operator +=( std::size_t offset );
 
private:
RandIt m_it;
size_t m_gap;
};
 
template<typename RandIt>
inline
gap_iterator<RandIt>::gap_iterator( RandIt it, std::size_t gap )
{
m_it = it;
m_gap = gap;
}
 
template<typename RandIt>
inline
gap_iterator<RandIt>::gap_iterator( gap_iterator const &other )
{
m_it = other.m_it;
m_gap = other.m_gap;
}
 
template<typename RandIt>
inline
typename gap_iterator<RandIt>::gap_iterator
&gap_iterator<RandIt>::operator =( gap_iterator const &rhs )
{
m_it = rhs.m_it;
m_gap = rhs.m_gap;
return *this;
}
 
template<typename RandIt>
inline
bool gap_iterator<RandIt>::operator !=( gap_iterator const &rhs )
{
return m_it != rhs.m_it;
}
 
template<typename RandIt>
inline
typename gap_iterator<RandIt>::value_type
&gap_iterator<RandIt>::operator *()
{
return *m_it;
}
 
template<typename RandIt>
inline
typename gap_iterator<RandIt>::value_type
*gap_iterator<RandIt>::operator ->()
{
return &*m_it;
}
 
template<typename RandIt>
inline
gap_iterator<RandIt> &gap_iterator<RandIt>::operator ++()
{
m_it += m_gap;
return *this;
}
 
template<typename RandIt>
inline
gap_iterator<RandIt> &gap_iterator<RandIt>::operator +=( std::size_t
offset )
{
m_it += offset * m_gap;
return *this;
}
 
#include <iostream>
#include <algorithm>
#include <cstdlib>
 
using namespace std;
 
int main()
{
int ai[16 * 16];
gap_iterator<int *> giiBegin( ai, 16 ),
giiEnd( giiBegin );
giiEnd += 16;
for_each( giiBegin, giiEnd, []( int &v ) { v = rand(); } );
for_each( giiBegin, giiEnd, []( int v ) { cout << v << " "; } );
}
Bonita Montero <Bonita.Montero@gmail.com>: Mar 28 08:46PM +0100

Why does this changed version not work ?
 
#include <iterator>
 
template<typename RandIt>
struct gap_iterator
{
using value_type = typename std::iterator_traits<RandIt>::value_type;
gap_iterator() = default;
gap_iterator( RandIt it, std::size_t gap );
gap_iterator( gap_iterator const &other );
gap_iterator &operator =( gap_iterator const &rhs );
bool operator !=( gap_iterator const &other );
value_type &operator *();
value_type *operator ->();
gap_iterator &operator ++();
gap_iterator &operator +=( std::size_t offset );
friend gap_iterator operator +( gap_iterator const &gi, std::size_t
offset );
 
private:
RandIt m_it;
size_t m_gap;
};
 
template<typename RandIt>
inline
gap_iterator<RandIt>::gap_iterator( RandIt it, std::size_t gap )
{
m_it = it;
m_gap = gap;
}
 
template<typename RandIt>
inline
gap_iterator<RandIt>::gap_iterator( gap_iterator const &other )
{
m_it = other.m_it;
m_gap = other.m_gap;
}
 
template<typename RandIt>
inline
typename gap_iterator<RandIt>::gap_iterator
&gap_iterator<RandIt>::operator =( gap_iterator const &rhs )
{
m_it = rhs.m_it;
m_gap = rhs.m_gap;
return *this;
}
 
template<typename RandIt>
inline
bool gap_iterator<RandIt>::operator !=( gap_iterator const &rhs )
{
return m_it != rhs.m_it;
}
 
template<typename RandIt>
inline
typename gap_iterator<RandIt>::value_type
&gap_iterator<RandIt>::operator *()
{
return *m_it;
}
 
template<typename RandIt>
inline
typename gap_iterator<RandIt>::value_type
*gap_iterator<RandIt>::operator ->()
{
return &*m_it;
}
 
template<typename RandIt>
inline
gap_iterator<RandIt> &gap_iterator<RandIt>::operator ++()
{
m_it += m_gap;
return *this;
}
 
template<typename RandIt>
inline
gap_iterator<RandIt> &gap_iterator<RandIt>::operator +=( std::size_t
offset )
{
m_it += offset * m_gap;
return *this;
}
 
template<typename RandIt>
inline
gap_iterator<RandIt> operator +( gap_iterator<RandIt> const &gi,
std::size_t offset )
{
gap_iterator<RandIt> ret( gi );
gi.m_it += offset * gi.m_gap;
return ret;
}
 
#include <iostream>
#include <algorithm>
#include <cstdlib>
 
using namespace std;
 
int main()
{
int ai[16 * 16];
gap_iterator<int *> giiBegin( ai, 16 ),
giiEnd( giiBegin + 16 );
//giiEnd += 16;
for_each( giiBegin, giiEnd, []( int &v ) { v = rand(); } );
for_each( giiBegin, giiEnd, []( int v ) { cout << v << " "; } );
}
Bonita Montero <Bonita.Montero@gmail.com>: Mar 28 08:47PM +0100

>     gi.m_it += offset * gi.m_gap;
ret.m_it += offset * ret.m_gap;
Bonita Montero <Bonita.Montero@gmail.com>: Mar 28 08:49PM +0100

Am 28.03.2020 um 20:47 schrieb Bonita Montero:
>>      gi.m_it += offset * gi.m_gap;
>      ret.m_it += offset * ret.m_gap;
ret.m_it = ret.m_it + offset * ret.m_gap;
The contained iterator might not know +=.
Bonita Montero <Bonita.Montero@gmail.com>: Mar 28 08:58PM +0100

Got it:
 
#include <iterator>
 
template<typename RandIt>
struct gap_iterator
{
using value_type = typename std::iterator_traits<RandIt>::value_type;
gap_iterator() = default;
gap_iterator( RandIt it, std::size_t gap );
gap_iterator( gap_iterator const &other );
gap_iterator &operator =( gap_iterator const &rhs );
bool operator !=( gap_iterator const &other );
value_type &operator *();
value_type *operator ->();
gap_iterator &operator ++();
gap_iterator &operator +=( std::size_t offset );
template<typename RandIt>
friend gap_iterator<RandIt> operator +( gap_iterator<RandIt> const
&gi, std::size_t offset );
 
private:
RandIt m_it;
size_t m_gap;
};
 
template<typename RandIt>
inline
gap_iterator<RandIt>::gap_iterator( RandIt it, std::size_t gap )
{
m_it = it;
m_gap = gap;
}
 
template<typename RandIt>
inline
gap_iterator<RandIt>::gap_iterator( gap_iterator const &other )
{
m_it = other.m_it;
m_gap = other.m_gap;
}
 
template<typename RandIt>
inline
typename gap_iterator<RandIt>::gap_iterator
&gap_iterator<RandIt>::operator =( gap_iterator const &rhs )
{
m_it = rhs.m_it;
m_gap = rhs.m_gap;
return *this;
}
 
template<typename RandIt>
inline
bool gap_iterator<RandIt>::operator !=( gap_iterator const &rhs )
{
return m_it != rhs.m_it;
}
 
template<typename RandIt>
inline
typename gap_iterator<RandIt>::value_type
&gap_iterator<RandIt>::operator *()
{
return *m_it;
}
 
template<typename RandIt>
inline
typename gap_iterator<RandIt>::value_type
*gap_iterator<RandIt>::operator ->()
{
return &*m_it;
}
 
template<typename RandIt>
inline
gap_iterator<RandIt> &gap_iterator<RandIt>::operator ++()
{
m_it += m_gap;
return *this;
}
 
template<typename RandIt>
inline
gap_iterator<RandIt> &gap_iterator<RandIt>::operator +=( std::size_t
offset )
{
m_it += offset * m_gap;
return *this;
}
 
template<typename RandIt>
inline
gap_iterator<RandIt> operator +( gap_iterator<RandIt> const &gi,
std::size_t offset )
{
gap_iterator<RandIt> ret( gi );
ret.m_it = ret.m_it + offset * ret.m_gap;
return ret;
}
 
#include <iostream>
#include <algorithm>
#include <cstdlib>
 
using namespace std;
 
int main()
{
int ai[16 * 16];
gap_iterator<int *> giiBegin( ai, 16 ),
giiEnd( giiBegin + 16 );
//giiEnd += 16;
for_each( giiBegin, giiEnd, []( int &v ) { v = rand(); } );
for_each( giiBegin, giiEnd, []( int v ) { cout << v << " "; } );
}
Jorgen Grahn <grahn+nntp@snipabacken.se>: Mar 28 08:51PM

On Sat, 2020-03-28, red floyd wrote:
> integers. For example, for the range [1, N), where N can be arbitrarily
> large. The problem to me appears to be that library algorithms only
> work on iterators.
 
Seems to me the real problem is you don't have iterators over [1, N).
 
Isn't there such a thing in Boost or something? If not, I imagine it
would be easy to write something so that:
 
const Range<int> r{1, 4711};
std::for_each(r.begin(), r.end(), f);
 
/Jorgen
 
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
xyz91987@gmail.com: Mar 28 07:47PM

Protect yourself and your loved ones !
KILL the coronavirus right now !
And save LOTS OF CASH
New tested, scientificly proven and amazing antivirus against coronavirus using Chloroquine and Colchicine
at very low price (33% discount)
Satisfaction garanteed or your money back !
 
http://als0p.atwebpages.com/coronavirus/coronavirus-en.php
 
Protégez-vous et vos proches!
TUEZ le coronavirus dès maintenant!
Et économisez BEAUCOUP D'ARGENT
Nouvel antivirus testé, scientifiquement prouvé et étonnant contre le coronavirus utilisant la Chloroquine et la Colchicine
à très bas prix (33% de réduction)
Satisfaction garantie ou agrent remie !
 
http://als0p.atwebpages.com/coronavirus/coronavirus-fr.php
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: