Safe pointers and Wt
Added by Noah Lopez over 9 years ago
Hi all,
For those interested, I've implemented a "safe" pointer that works with Wt. It acts pretty much just like a native pointer, except that it throws an exception when attempting an invalid dereference. And, of course, it doesn't support pointer arithmetic. It doesn't take ownership of the target (like std::shared_ptr). It's generally a straight drop-in substitute for native pointers. Really easy to use. As an example, I've converted the "mission" Wt example. You can see it here: https://github.com/duneroadrunner/wt_example_safer_mission
These safe pointers, or "registered" pointers as I call them, handle the case where you are trying to access a WWidget that has already been deleted by the Wt library. So for example, you might have something like this:
auto text1_ptr = new WText("Text1");
bindWidget("label1", text1_ptr);
...
auto text2_ptr = new WText("Text2");
bindWidget("label1", text2_ptr); /* This is presumably going to cause the Wt library to delete the thing that text1_ptr points to. */
...
text1_ptr->setText("New Text1"); /* So this is presumably going to access invalid memory. */
This example arguably demonstrates that the Wt interface is inherently "unsafe". But using registered pointers you can instead write:
auto text1_ptr = mse::registered_new<WText>("Text1"); /* text1_ptr will be a "registered" pointer, not a native pointer. */
bindWidget("label1", (WText *)text1_ptr);
...
auto text2_ptr = mse::registered_new<WText>("Text2");
bindWidget("label1", (WText *)text2_ptr); /* This is presumably going to cause the Wt library to delete the thing that text1_ptr
points to. The difference is that text1_ptr will be "immediately notified" of the deletion. (This accomplishment relies on
the fact that WWidget's destructor is virtual.) */
...
text1_ptr->setText("New Text1"); /* This will throw an exception rather than try to access invalid memory. */
So using these registered pointers kind of transforms (part of) the Wt library interface from an inherently unsafe one to a safe one (without actually modifying the Wt library in any way). Possibly making it slightly more acceptable to security-minded web developers?
I noticed in a previous post that you guys are planning on "breaking the api" for version 4 in order to modernize it. Presumably functions like bindWidget() will take an std::shared_ptr (or some facsimile) instead of a (WWidget*)? I'll point out that you could define Wt::wwidget_new<>, Wt::wwidget_ptr<> and Wt::wwidget_delete<> right now to allow Wt users to (partially) future proof their code for the new api. Right? I mean in version 3.3.7 or whatever add this:
template<class _Ty, class... Args>
_Ty *Wt::wwidget_new<_Ty>(Args&&... args) { return new _Ty(args...); }
template<class _Ty>
typedef _Ty* Wt::wwidget_ptr<_Ty>;
/* Presumably Wt::wwidget_delete() will rarely be used as the Wt library generally takes ownership of WWidgets. */
template <class _Ty>
void Wt::wwidget_delete(const _Ty* ptr) { delete ptr; }
and change all the Wt examples to use these. Then when version 4 comes out, change it to:
template<class _Ty, class... Args>
std::shared_ptr<_Ty> Wt::wwidget_new<_Ty>(Args&&... args) { return std::make_shared<_Ty>(args...); }
template<class _Ty>
typedef std::shared_ptr<_Ty> Wt::wwidget_ptr<_Ty>;
template <class _Ty>
void Wt::wwidget_delete(const &std::shared_ptr<_Ty> ptr) {}
and all the Wt examples should continue work with version 4 without modification. Well, with respect to WWidget pointers anyway. Right?
But as an intermediate, before version 4 comes out, you can define Wt::wwidget_new<>, Wt::wwidget_ptr<> and Wt::wwidget_delete<> to use "registered" pointers instead to enhance safety while still remaining compatible with legacy code.
Btw, just to be clear, registered pointers can replace native pointers pretty much anywhere, not just in the Wt interface. There are more examples here: https://github.com/duneroadrunner/SaferCPlusPlus
Replies (3)
RE: Safe pointers and Wt - Added by Koen Deforche over 9 years ago
Hey,
Great! We are indeed considering something in Wt 4 to solve exactly the same problem that you've solved.
We hadn't thought of this method, since we were thinking more along the lines of the current system which tracks the lifetime of objects (as used to determine when to invalidate a signal/slot connection). In that way, a 'wwidget_new<>' would not even be needed. But it's a nice approach as it's generic in its own way.
Koen
RE: Safe pointers and Wt - Added by Noah Lopez over 9 years ago
By "'wwidget_new<>' would not even be needed" do you mean that you're planning on retaining the native pointer interface? Personally, I'd vote for an intrinsically "safer" interface (like one that uses some facsimile of std::shared_ptr). Or at least one that could be transparently upgraded to be safer at some point in the future. Just my 2 cents.
RE: Safe pointers and Wt - Added by Koen Deforche over 9 years ago
No, unlike what seems to be the current sentiment, that a raw pointer is okay for 'observing/no ownership', I'm more inclined to also say that you should use a smart pointer for this, using the same thinking you have.