As of C++17 you can: http://en.cppreference.com/w/cpp/error/assert
You can also throw exceptions in constexpr functions since C++14.
1,770 karma · joined March 26, 2015
As of C++17 you can: http://en.cppreference.com/w/cpp/error/assert
You can also throw exceptions in constexpr functions since C++14.
I used Welsh, Irish, Catalan and French-in-Quebec as examples because they're all languages that declined (to varying degrees) over several centuries under (varying degrees of) government suppression, experienced a partial rebound in interest and popularity in the 20th century after government policy was changed to encourage their use, but ultimately returned to the same trend of declining usage, something like
100|
N
|\_
| \_
| \_ ____
| \_ _/ \_
| \____/ \
N_______________________
0|suppressed encouragedWhere the government intervenes in the opposite direction, the transition can be much more rapid. Visiting Strasbourg (in Alsace, France), people's surnames, street and place names, and the local cuisine are all German, but nobody speaks a word of it. It was amazing (and slightly depressing) to see how in 2-3 generations a city could forget the language it spoke for nigh-on 1,500 years.
The way things are going, I wouldn't be surprised if Dutch were considered a dying language 50 years from now.
The reason transmute is (particularly) unsafe is because it can be quite difficult to tell whether a variable is bound to a value, a (possibly implicitly-dereferenced) reference or a slice, and so, given transmute's implicit type inference, it's very easy to quietly get either the source or destination type very wrong.
The size-checking is also I would argue useless bordering on worse than useless (false sense of security) since any two references or two slices are the same size, but are unlikely to actually be interconvertible, and it also fails to check alignment.
C and C++ (particularly the latter) make it much more difficult to inadvertently cast between values and pointers, and the strict aliasing rules (aliasing almost always illegal except through `char`) are draconian enough to discourage the practice of type-punning-through-indirection (and ensuing alignment bugs) altogether.
`memcpy` (and unions-as-implemented) is nice because it's explicit and it just works.
> I'm curious if you've got a defect report link or similar
I'm having a hard time finding the detailed original paper pointing it out, but see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p059...
Depending on what you mean by "raw memory manipulation" that can actually be surprisingly difficult with C, without running into undefined behavior, at least technically-speaking. e.g. type-punning through unions is defined by all implementations I can think of, but technically UB according to the standard.
That said, if you know the UB rules, C/C++ are still the "nicest" mainstream languages to use for that sort of super-low-level manipulation of raw object representations. The non-UB type-punning methods in C/C++ (`memcpy(3)`, casting addresses to pointer-to-`char`), finicky as they are, are still more ergonomic and, ironically, safer than Rust's poorly-designed `mem::transmute()` API.
Language-lawyering is quite the rabbit hole, BTW; for example, it was discovered a few years ago that the wording of the formal C++ memory model defined in the C++11 and later standards means all non-trivial C programs (technically-speaking, not according to any existing or sane implementation) invoke undefined behavior in C++. We'll be lucky if the bikeshedding over the fix for this gets resolved in time to make C++20.
#include <stdlib.h>
void *safe_ptr(void *p) {
if (!p) exit(1);
return p;
}
#define INIT(TYPE) safe_ptr(malloc(sizeof(TYPE)))
int *get_intbuffer(void) {
enum {N=3};
int *const intbuffer = (int*)INIT(int[N]);
int i = N;
/* Technically UB in C++; lifetime starts
* w/ declaration or initializing w/ new
* or placement-new, so reads/writes thru
* the result of malloc w/o initializing
* the objects w/ placement-new are UB */
while (i--) intbuffer[i] = i;
return intbuffer;
}Apples to oranges comparison of course, since Rayon isn't [planned to become?] part of the Rust standard library.
[1] http://www.bfilipek.com/2017/08/cpp17-details-parallel.html#...
New link: https://godbolt.org/g/8uGn4h
Changing the loop to 4 iterations for compactness' sake, (aligned) structs of two u64s generate the following, vectorized code:
vmovdqu (%rsi), %xmm0
vpaddq (%rdi), %xmm0, %xmm0
vmovdqu %xmm0, (%rdx)
vmovdqu 16(%rsi), %xmm0
vpaddq 16(%rdi), %xmm0, %xmm0
vmovdqu %xmm0, 16(%rdx)
vmovdqu 32(%rsi), %xmm0
vpaddq 32(%rdi), %xmm0, %xmm0
vmovdqu %xmm0, 32(%rdx)
vmovdqu 48(%rsi), %xmm0
vpaddq 48(%rdi), %xmm0, %xmm0
vmovdqu %xmm0, 48(%rdx)
retq
And if the pointer arguments are declared `restrict`, the loop can be vectorized even more aggressively: vmovdqu64 (%rsi), %zmm0
vpaddq (%rdi), %zmm0, %zmm0
vmovdqu64 %zmm0, (%rdx)
vzeroupper
retq
Either of which is much more efficient than the code generated for unaligned, packed 96-bit structs: movq (%rsi), %rax
addq (%rdi), %rax
movq %rax, (%rdx)
movl 8(%rsi), %eax
addl 8(%rdi), %eax
movl %eax, 8(%rdx)
movq 16(%rsi), %rax
addq 16(%rdi), %rax
movq %rax, 16(%rdx)
movl 24(%rsi), %eax
addl 24(%rdi), %eax
movl %eax, 24(%rdx)
movq 32(%rsi), %rax
addq 32(%rdi), %rax
movq %rax, 32(%rdx)
movl 40(%rsi), %eax
addl 40(%rdi), %eax
movl %eax, 40(%rdx)
movq 48(%rsi), %rax
addq 48(%rdi), %rax
movq %rax, 48(%rdx)
movl 56(%rsi), %eax
addl 56(%rdi), %eax
movl %eax, 56(%rdx)
retq
A smaller cost is that in non-vector code, using a 64-bit register (rax) in 32-bit mode (eax) is wasting half of the register.IIRC, unaligned loads and stores will also, at the hardware level, stall the pipeline and inhibit out-of-order execution.
uint64_t n = 0xdeadbeef;
uint32_t foo = (uint32_t)n; // OK
uint32_t *bar = (uint32_t*)&n; // "OK" but useless
foo = *bar; // undefined behavior!!!
uint8_t *baz = (uint8_t*)&n;
uint8_t byte = *baz; // OK, uint8_t is `unsigned char`
// Same-size integral types are OK
const volatile long long p = (const volatile long long*)&n;
const volatile long long cvll = *p; // well-defined
2. Structs are aligned to the member with the strictest alignment requirement, so a struct of a `uint64_t` and a `uint32_t` will be aligned on an 8-byte boundary, meaning its size will be 128 bits.Tl;dr is that only one person (the neo-Nazi perennial candidate) filed to run in the GOP primary in a safe-D seat, and state law precludes a write-in campaign. Democrats also occasionally get embarrassing nominees this way, especially in safe seats, e.g.
https://en.wikipedia.org/wiki/Kesha_Rogers https://en.wikipedia.org/wiki/Mark_E._Clayton
Yes and no; multithreading and optimizing to remove branches can do a number on readability, but I find optimizing for cache locality and compile-time evaluation often makes code more readable. It depends on the language obviously.
The interpreter's stack?
Java's even more verbose, so it probably has the stronger claim on German,* which I guess makes C++ Danish and Perl English. :)
#define SCOPE_EXIT(...)\
struct SE##__LINE__##_{~SE##__LINE__##_(){__VA_ARGS__;}}se##__LINE__##_1. Stack and static-allocated arrays.
2. True pass-by-reference for value types.
3. A sufficiently-smarter compiler that's more often able to elide dynamic allocations, reference counting, bounds checks, vtables etc. (Lifetime semantics will help with this).
It's an invalid reference in that a reference which does not refer to an object is either dead code, or an unrecoverable error.
There's no point in checking if a reference refers to null, because (unlike pointers) it cannot then be assigned a useful value, and also because any such check will be removed by the optimizer, because null references are undefined behavior (in both C++[1] and Rust[2]).
> Indeed, though in Rust (like most other safe-by-default languages) you do need that glaringly obvious "unsafe" keyword to perform the conversion, which is a hint that you should be checking for null at that point.
It is an improvement but (obviously) the unsafety in this case escapes from the "unsafe" block into the surrounding "safe" code and other "safe" functions.
It's also possible, even in entirely-"safe" code, to crash a Rust program by attempting to dereference a null pointer.
fn dereference(nullable_ref: Option<&i32>) -> &i32 {
nullable_ref.unwrap()
}
fn crash() {
dereference(None);
}
[1] https://godbolt.org/g/TjexTpI'll amend my statement to "unlike pointers, valid references cannot point to a null address."
This is also true of any other language which has both nullable and non-nullable references, and a way to convert the former to the latter without accounting for the null case, such as Rust[1].
fn pass_null_reference() {
unsafe { print_i32(&*std::ptr::null()) }
}
fn print_i32(n: &i32) {
println!("{}", n) // segfault
}
[1] https://godbolt.org/g/1oUnNtSo you can write
void transform_string(char s[], char transform(char));
instead of having to write void transform_string(char s[], char(*transform)(char));
When writing C++ you should use function references when possible, as references cannot be null. void transform_string(char s[], char(&transform)(char));
Function references unfortunately require the more complex syntax, but fortunately C++11 `using`-style typedefs are quite simple to use with function types. using TransformFunc = char(char, char);
typedef char TransformFunc(char, char); // C-style typedef, same as above
// Function pointer
TransformFunc fptr = /* ... */
// Function reference
TransformFunc &fref = /* ... */Mostly true under the hood; optional value types are tagged unions (I didn't know this before reading this article and checking for myself), but optional reference types are still, as you'd expect, nullable pointers.[1]
[1] https://www.seniorliving.org/history/1900-2000-changes-life-...