Closed MirrorGuard closed 6 years ago
The library has no conversion support for primitive pointer types (e.g. int*
or float*
) intentionally.
Sure, one would add such a converter:
// helper meta function
template<typename T>
using is_character_type = std::integral_constant<bool,
std::is_same<T, char>::value ||
std::is_same<T, char16_t>::value ||
std::is_same<T, wchar_t>::value>;
// converter for non-const T* that is not wrapped and not character type
template<typename T>
struct convert<T*, typename std::enable_if<
!is_wrapped_class<T>::value &&
!is_character_type<T>::value &&
!std::is_const<T>::value>::type>
{
using from_type = T*;
//using to_type = decltype(convert<T>::to_v8(std::declval<v8::Isolate*>(), std::declval<T>()));
using to_type = v8::Local<v8::Value>;
static bool is_valid(v8::Isolate*, v8::Local<v8::Value> value)
{
return true;
}
static from_type from_v8(v8::Isolate* isolate, v8::Local<v8::Value> value)
{
if (value.IsEmpty() || value->IsNullOrUndefined())
{
return nullptr;
}
return &(convert<T>::from_v8(isolate, value)); // (1)
}
static to_type to_v8(v8::Isolate* isolate, T* value)
{
if (!value) return v8::Null(isolate); // (2)
else return convert<T>::to_v8(isolate, *value);
}
};
But there are 2 issues in this converter:
from_v8()
function, because at the line (1) it returns an address of temporary value (might be related to #39)to_v8()
function, because at the line (2) it is v8::Local<v8::Primitive>
and we have to use least common denominator v8::Local<v8::Value>
In my opinion, usage of std::optional<T>
for optional return values, and std::array
for arrays looks better.
Sorry for my late response if it is too late.
I completely agree with you, that retuning std::optional
and std::array
are much better than raw pointers in the most cases.
But unfourtenatly my question related to the least part of cases, such as external APIs, third party libraries that are at least difficult or at most impossible to rewrite.
I didnt talk about exactly providing a way to deal with raw pointers inside javascrpt. But I'm looking for a way to provide optionality and or access to internal data, that is returned optionally. And as I talked alredy, I assume an external API, not my own code, that I would like to bind, something not opensource and/or something with a large code base.
Just to do in javascript something like (in a context of optionality: "null or a value") this:
var v=fuctionFromCpp();
if(v==null)
/*do something if no any value*/
else{
/*working with value here*/
}
whereas in C++ I have an external API like this:
//That is documented, that this function (or a method) returns a NULL or a pointer to the valid value
//and this is not required to delete/free this manually.
//And there is a strict guarantee that the *target* the pointer points to is existing enough time
//to work with it (including in javascript too).
//But the problem is: how to represent it's optionality?
T* functionInCpp();//Or a class method.
I know, according to v8pp documentation, that there is a way to wrap std::vector
and std::array
, but im not sure about std::optional
. Moreover, my request about pointers covertion (where "pointer" is a representation of an optional value and does the same job as an exact std::optional
) is very similar to convertion of a an exact std::optional
.
So, may be it would be better to ask, "if there in v8pp a convertion of an std::optional
?"
And if so, why there is no convertion of the raw pointer T* the same way as std::optional
About the lines (1) and (2) you wrote:
What about v8::Object and it's internal fields (I mean the Get/Set AlignedPointerFromInternalField
)?
(1)
return (value->IsObject())?
static_cast<from_type>(Object::Cast(value)->GetAlignedPointerFromInternalField(INDEX)):
nullptr;
I'm not sure about exact index, but in my opinion, conceptually this is the right direction to solve the optionality problem.
(2) Again this looks like that it should be an v8::Object.
For the most cases that is assumed, that T is the pointer to an object of T the type (struct or a class). So, there is no much difference between the wrapping that v8pp does and has to be done, except the setting and getting internal pointer (exact origial* pointer C++ to the C++ object).
Unfortunately for primitives (such as int*, for example) the tiny workaround required:
//For simplicity this is not a template and I assume that T=int.
//Also I've discard specific almost all int operations (except one) and a wrapping code,
//where class RefWrapperInt wrapped with v8pp::class_<RefWrapperInt> - just for short.
class Ref_wrapper_int{
public:
Ref_wrapper_int(int * v):v_(v){}
//operation example
void add(int a){
if(v_)*v_+=a;
}
//this is for implicit convertion to int*
operator int*(){
return v_;
}
private:
int * v_;
}
And so, if T == int,
at the (1) line from_v8
is a
Ref_wrapper_int from_v8(Isolate*, Handle<Value>)
and at the (2) line to_v8
is a
v8::Handle<Object> to_v8(Isolate*, int*)
Remark:
For primitives there is no InternalFields (AlignedPointerFromInternalField) required,
so my first example:
return (value->IsObject())? static_cast<from_type>(Object::Cast(value)->GetAlignedPointerFromInternalField(INDEX)): nullptr;
wich is not adapted for primitives, is incorrect in case of primitives, wrapped using workaround similar to the Ref_wrapper_int from my example. But this are details.
Is there all clear in my reasoning, or I've missed somthing important (or have a mistake)?
Well, this Ref_wrapper_int
seems much like std::optional<int>
for me. And it seems that you will have to writer wrapper functions/lambdas to deal with pointers to optional objects from a 3rd party library. Sorry, I can't see a magic universal conversion for that.
Ок. This is not exactly what I would like to know, but it is a fact, so I have nothing to do than to close this issue, because of impossibility to solve it in a convenient way. You spent a time during answer for my questions, so thank you not for a solution, but for your answers.
In the most cases pointers are converted to an array/vector/string (e.t.c), but sometimes that is wrong behavior and I have found no way to workaround it. It is so, when pointers are used as an optional references (when null-value is a possible; as something like references in a Java or a C#, for example).
Following pseudocode snippet shows what am I talking about:
For class WithOptionalData,
const T*
means, that it mostly returns data, but "null-value" is possible. Usingstd::optional
instead ofT*
sometimes impossible or inefficient:In fact, with class WithOptionalData, while using:
compile time error occur with v8pp::to_v8, deep inside in v8pp library:
forward_ret
(second overload)r_property_impl<Get, Set, true>::get_impl
(first overload)Maybe I am doing wrong something, but there is no documented way to deal with methods, that returns pointers to requested values (when pointer means "optional", not an array).
Other (second) problem with pointers looks like this:
While binding, using
v8pp::class_
:It looks like that code above should work, but it doesnt. Despite that
getMatrix
returns an array and it is a correct behavior (and v8pp can deal with arrays), something goes wrong and it looks like thatconst float*
processed as a special kind of a string (where character represented as a float-value). Evidently, that it is a constant pointer to the internal array, not a some kind of string. But deeply inside the library,const float *
is processed as a string, not an array of float, that is wrong and leads to compile time error deep inside v8pp, somewhere with to_v8.Although there is a way to workaround this (second problem), using static functions/lamdas, like
but it is unusual and inconvenient way to do such things.
Anyway, is there a way to deal with pointers using v8pp, when: 1)Pointer points to an optional data (when data or nullptr is returned) 2)Pointer is a const primitive pointer to an array and it looks like that it is processed as a some kind of string (instead of a correct behavior, when it is processed as an array/vector, like in example with a
const float*
) ?Thank you in advance for your answer.