3 ms·
> Create a vector. Push an element onto it. Take a reference to that element with operator[]. Clear the vector. Call a method on that dangling reference. > Cre
by duneroadrunner 10y ago
> Create a vector. Push an element onto it. Take a reference to that element with operator[]. Clear the vector. Call a method on that dangling reference.
> Create an object on the stack. Return a reference to that object. Call a method on that reference.
References are one of the unsafe C++ elements that SaferCPlusPlus is intended to be used to replace [1].
> Create a vector. Push an element onto it. Call a method on that element that clears the vector and then calls another virtual method on itself, via the this pointer.
Yes, that series of operations is safe. A related example from the "msetl_example.cpp" file:
typedef mse::mstd::vector<int> vint_type;
mse::mstd::vector<vint_type> vvi;
{
vint_type vi;
vi.push_back(5);
vvi.push_back(vi);
}
auto vi_it = vvi[0].begin();
vvi.clear();
try {
/* At this point, the vint_type object is cleared from vvi, but it has not been deallocated/destructed yet because it
"knows" that there is an iterator, namely vi_it, that is still referencing it. At the moment, std::shared_ptrs are being
used to achieve this. */
auto value = (*vi_it); /* So this is actually ok. vi_it still points to a valid item. */
assert(5 == value);
vint_type vi2;
vi_it = vi2.begin();
/* The vint_type object that vi_it was originally pointing to is now deallocated/destructed, because vi_it no longer
references it. */
}
catch (...) {
/* At present, no exception will be thrown. We're still debating whether it'd be better to throw an exception though. */
}
I agree with the gist though. This kind of thing should be prevented at compile time. Rust has an excellent static analyzer/enforcer built into its compiler. Arguably, it would be a service to the community to unbundle it from the Rust compiler and make it available for application to C++ code as well. Arguably.
> Accidentally share a vector between threads. Race push_back() and remove().
SaferCPlusPlus addresses the sharing of objects between asynchronous threads [2]. A particular shortcoming of C++ wrt to object sharing is that it doesn't have a notion of "deep const/immutability".
> Additionally, the pointer registration mechanism that that library uses has a runtime performance cost worse than a GC write barrier (because it incurs writes on reads).
Um, yeah, modern code should try to avoid the use of general pointers (and generally does). Most modern languages don't provide general pointers. SaferCPlusPlus makes them safe and slow (and available for easy porting of legacy code). When writing new code you would instead, when required, use one of the faster pointer types available in the library.
Don't interpret SaferCPlusPlus as an assertion that C++ is a uniformly better language than Rust or other modern languages. It's more of a suggestion that C++ and existing C++ code bases can be salvaged to a greater degree than one might think.
[1] http://www.codeproject.com/Articles/1093894/How-To-Safely-Pass-Parameters-By-Reference-in-Cplu http://www.codeproject.com/Articles/1093894/How-To-Safely-Pa...
[2] http://www.codeproject.com/Articles/1106491/Sharing-Objects-Between-Threads-in-Cplusplus-the-S http://www.codeproject.com/Articles/1106491/Sharing-Objects-...
- pcwalton 10y ago> References are one of the unsafe C++ elements that SaferCPlusPlus is intended to be used to replace [1]. OK, so you can't use references. Then, as I said before, your pointer replacements have a runtime performance cost worse than GC write barriers. > Yes, that series of operations is safe. A related example from the "msetl_example.cpp" file: I don't think you understood me. I mean the this pointer. "this" is hardwired into C++ to be an unsafe pointer. > I agree with the gist though. This kind of thing should be prevented at compile time. Rust has an excellent static analyzer/enforcer built into its compiler. Arguably, it would be a service to the community to unbundle it from the Rust compiler and make it available for application to C++ code as well. Arguably. Not possible. It's totally incompatible with existing C++ designs. > Um, yeah, modern code should try to avoid the use of general pointers (and generally does). Most modern languages don't provide general pointers. I think you're getting lost in the weeds of what a "general pointer" is and is not. It doesn't matter. The point is that if your references track their owners at runtime, then you are just creating a GC. If the overhead of doing that is worse than a traditional GC (which, if you are doing that much bookkeeping, it will be), then there's little purpose to it.
- duneroadrunner 10y ago> OK, so you can't use references. Then, as I said before, your pointer replacements have a runtime performance cost worse than GC write barriers. The library provides three types of pointers - "registered", "scope" and "refcounting". I believe you are referring to the registered pointers, that indeed have significant cost on construction, destruction and assignment. But registered pointers are really mostly intended to ease the task of initially porting legacy code. New or updated code would instead use either "scope" pointers, which point to objects that have (execution) scope lifetime, or "refcounting" pointers. Scope pointers have zero extra runtime overhead, but are (at the moment) lacking the needed "static enforcer" to ensure that scope objects are indeed allocated on the stack. (Their type definition does prevent a lot of potential inadvertent misuse, but not all. And Ironclad C++ does have such a static enforcer.) > I don't think you understood me. I mean the this pointer. "this" is hardwired into C++ to be an unsafe pointer. You're right, that's a good point. But really it's a practical issue rather than a technical one. I mean technically, use of the "this" pointer should be replaced with a safer pointer, just like any other native pointer. For example this is technically one of the safe ways to implement it in SaferCPlusPlus: class CA { public: template<class safe_this_pointer_type, class safe_vector_pointer_type> void foo1(safe_this_pointer_type safe_this, safe_vector_pointer_type vec_ptr) { vec_ptr->clear(); /* The next line will throw an exception (or whatever user specified behavior). */ safe_this->m_i += 1; } int m_i = 0; } void main() { mse::TXScopeObj<mse::mstd::vector<CA>> vec1; vec1.resize(1); auto iter = vec1.begin(); iter->foo1(iter, &vec1); } That is, technically, if you're going to use the "this" pointer, explicitly or implicitly, you should pass a safe version of it (in this case "iter"). But yeah, in practice I don't expect people to be so diligent. I wonder how often this type of scenario arises in practice? So do I understand correctly that the Rust language allows for the same type of code, but the compiler won't build it unless it can statically deduce that it is safe? > Not possible. It's totally incompatible with existing C++ designs. Even if you prohibit the unsafe elements? Including (implicit and explicit) "this" pointers?