I have some code that looks like this:
class Widget {
public:
static std::unique_ptr<Widget> make(OtherArgs args); // factory pattern
Widget(v8::isolate&& isolate, OtherArgs args);
private:
v8::Local<v8::Object> Widget::create_v8_object()
v8::isolate isolate_;
v8::Local<v8::Object> gadget_;
};
std::unique_ptr<Widget> Widget::make(OtherArgs args)
{
v8::isolate isol;
v8::HandleScope scope(isol.get());
return std::make_unique<Widget>(std::move(isol), args);
}
Widget::Widget(v8::isolate&& isol, OtherArgs args) :
isolate_(std::move(isol)),
context_(isolate_.get()),
gadget_(isolate_.get(), create_v8_object())
{
}
v8::Local<v8::Object> Widget::create_v8_object()
{
v8::Local<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(isolate_.get());
// ...
v8::Local<v8::Object> gadget = v8::Local<v8::Object>::New(isolate_.get(), tmpl->NewInstance());
// ...
return gadget;
}
int main()
{
auto widget = Widget::make(some_args);
// ...
}
However, the three-line Widget::make
function is ugly — it's the single "blessed" way to create Widget
objects, but we cannot make the Widget
constructor private because Widget::make
is implemented in terms of make_unique
.
However, if we could make our Widget
constructor itself create "blessed" objects, we could change main()
to
int main()
{
auto widget = std::make_unique<Widget>(some_args);
// ...
}
and it would all Just Work, with no factory function.
The problem I'm having is that I believe we need the v8::HandleScope
surrounding the construction of all those v8::Local<T>
s inside create_v8_object()
, which is called from our constructor's member-initializer-list. We need a way to create a HandleScope
before executing the member-initializers, and then destroy it at the end of the constructor.
I'm looking for a way to solve this problem with a maximum of code clarity. (For example, I could add a member handleScope_
to the class, and make sure its member-initializer is the first in the list (well, the second, after isolate_
); but that would bloat the size of the class and I wouldn't know how to clean it up at the end of the constructor.
I've also thought of moving the HandleScope
from Widget::make
down into Widget::create_v8_object()
, where it would become an EscapableHandleScope
and we'd return scope.Escape(gadget)
. But if I have lots of create_v8_object
calls, would all those HandleScopes impose a performance penalty, or have any other bad effect?