2012-06-20 99 views
1

我目前正在嘗試製作一組​​轉換函數,通過一次調用,可以(嘗試)將JavaScript對象(CefV8Value)轉換爲其C++對象,並支持指針。模糊不清的模板功能專業化編譯錯誤

這裏是轉換功能(末尾指針轉換):

template<typename T> 
T convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value) {}; 

// Explicit type conversion functions 
#define V8VALUE_TO_CPP_CONVERSION(type) \ 
     template<> type \ 
     convert_v8value_to_cpp<type>(const CefRefPtr<CefV8Value> &value) 

V8VALUE_TO_CPP_CONVERSION(CefRefPtr<CefV8Value>) 
{ 
    return value; 
} 

V8VALUE_TO_CPP_CONVERSION(bool) 
{ 
    return value->GetBoolValue(); 
} 

V8VALUE_TO_CPP_CONVERSION(int) 
{ 
    return value->GetIntValue(); 
} 

V8VALUE_TO_CPP_CONVERSION(std::string) 
{ 
    return value->GetStringValue().ToString(); 
} 

V8VALUE_TO_CPP_CONVERSION(const char *) 
{ 
    return value->GetStringValue().ToString().c_str(); 
} 

V8VALUE_TO_CPP_CONVERSION(std::wstring) 
{ 
    return value->GetStringValue().ToWString(); 
} 

// HACKHACK: most VGUI functions take non-const wchar_t pointers, when they 
// shouldn't 
V8VALUE_TO_CPP_CONVERSION(wchar_t *) 
{ 
    return (wchar_t*)value->GetStringValue().ToWString().c_str(); 
} 

V8VALUE_TO_CPP_CONVERSION(const wchar_t *) 
{ 
    return value->GetStringValue().ToWString().c_str(); 
} 

V8VALUE_TO_CPP_CONVERSION(double) 
{ 
    return value->GetDoubleValue(); 
} 

V8VALUE_TO_CPP_CONVERSION(float) 
{ 
    return value->GetDoubleValue(); 
} 

//----------------------------------------------------------------------------- 
// Purpose: converts a JS array to a C++ vector (of type T) 
//----------------------------------------------------------------------------- 
template<typename T> 
std::vector<T> convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value) 
{ 
    std::vector<T> vec; 

    if(!value->IsArray()) 
     return vec; 

    for(int i = 0; i < value->GetArrayLength(); ++i) 
    { 
     CefRefPtr<CefV8Value> element = value->GetValue(i); 
     vec.push_back(convert_v8value_to_cpp<T>(element)); 
    } 

    return vec; // hopefully move semantics will optimise this and not copy-construct 
} 

//----------------------------------------------------------------------------- 
// Purpose: converts a JS object to a C++ pointer (where T is a pointer type) 
//----------------------------------------------------------------------------- 
template<typename T> 
typename std::enable_if<std::is_pointer<T>::value, T>::type 
    convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value) 
{ 
    if(!value->IsObject()) 
     return NULL; 

    CefRefPtr<CefV8Value> pTypeId = value->GetValue("__v8bind_typeid__"); 
    if(!pTypeId || !pTypeId->IsString()) 
     return NULL; 

    CefRefPtr<CefV8Value> pPointerVal = value->GetValue("__v8bind_ptr__"); 
    if(!pPointerVal || !pPointerVal->IsInt()) 
     return NULL; 

    if(pTypeId->GetStringValue().ToString() == typeid(T).name()) 
     return (T)pPointerVal->GetIntValue(); 

    return NULL; 
} 

下面是一個使用該指針函數的代碼:

WrapClass *pThis = convert_v8value_to_cpp<WrapClass*>(object); 

的Visual Studio抱怨說:

error C2668: 'convert_v8value_to_cpp' : ambiguous call to overloaded function 
binding_test.cpp(106): could be 'Point *convert_v8value_to_cpp<WrapClass*>(const CefRefPtr<T> &)' 
with 
[ 
    WrapClass=Point, 
    T=CefV8Value 
] 

binding_test.cpp(88): or  'std::vector<_Ty,_Ax> convert_v8value_to_cpp<WrapClass*>(const CefRefPtr<T> &)' 
with 
[ 
    _Ty=Point *, 
    _Ax=std::allocator<Point *>, 
    WrapClass=Point, 
    T=CefV8Value 
] 

binding_test.cpp(31): or  'T convert_v8value_to_cpp<WrapClass*>(const CefRefPtr<CefV8Value> &)' 
with 
[ 
    T=Point *, 
    WrapClass=Point 
] 

while trying to match the argument list '(CefRefPtr<T>)' 
with 
[ 
    T=CefV8Value 
] 

我不明白d呼叫如何模糊(除WrapClass *T的第一個轉換功能匹配外)。但是它也表示可能的候選人是std::vector轉換。這怎麼可能?

非常感謝提前!

+2

見下面的答案。此外,您轉換爲char *,wchar_t *和wchar_t const *會返回指向臨時的指針,這是未定義的行爲。 – Tomek

+0

我沒有注意到,我原本以爲[W] String()函數返回引用......謝謝Tomek! – Saul

回答

6

這兩種:

template<typename T> 
std::vector<T> convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value) 
{ ... } 

template<typename T> 
typename std::enable_if<std::is_pointer<T>::value, T>::type 
convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value) 
{ ... } 

不與主要功能模板函數部分特例(未無論如何不允許),但超載,所以他們一起是所有三個曖昧,因爲他們只從不同彼此在返回類型。

你希望你的函數模板convert_v8value_to_cpp委託給一個靜態函數(do_it(),說)在類模板convert_v8value_to_cpp_helper,因爲不像函數模板類模板可以專業化。

主類模板:

template <typename T> 
struct convert_v8value_to_cpp_helper {}; // no do_it() here by intention 

全部專業:

// Explicit type conversion functions 
#define V8VALUE_TO_CPP_CONVERSION(type, code) \ 
template <> struct convert_v8value_to_cpp_helper<type> { \ 
    static T do_it() code \ 
} 

V8VALUE_TO_CPP_CONVERSION(bool, {return value->GetBoolValue();}); 
V8VALUE_TO_CPP_CONVERSION(int, {return value->GetIntValue(); }); 
V8VALUE_TO_CPP_CONVERSION(std::string, 
{ return value->GetStringValue().ToString(); }); 
V8VALUE_TO_CPP_CONVERSION(const char *, 
{ return value->GetStringValue().ToString().c_str(); }); 
V8VALUE_TO_CPP_CONVERSION(std::wstring, 
{ return value->GetStringValue().ToWString(); }); 
// HACKHACK: most VGUI functions take non-const wchar_t pointers, when they 
// shouldn't 
V8VALUE_TO_CPP_CONVERSION(wchar_t *, 
{ return (wchar_t*)value->GetStringValue().ToWString().c_str(); }); 
V8VALUE_TO_CPP_CONVERSION(const wchar_t *, 
{ return value->GetStringValue().ToWString().c_str(); }); 
V8VALUE_TO_CPP_CONVERSION(double, {return value->GetDoubleValue();}); 
V8VALUE_TO_CPP_CONVERSION(float, {return value->GetDoubleValue();}); 

專業化面向std::vector(包括那些具有自定義分配器):

template<typename T, typename A> 
struct convert_v8value_to_cpp< std::vector<T,A> > { 
    static std::vector<T,A> do_it(const CefRefPtr<CefV8Value> &value) 
    { 
     std::vector<T,A> vec; 

     if (!value->IsArray()) 
      return vec; 

     for(int i = 0; i < value->GetArrayLength(); ++i) 
     { 
      CefRefPtr<CefV8Value> element = value->GetValue(i); 
      vec.push_back(convert_v8value_to_cpp<T>(element)); 
     } 

     return vec; // return value optimisation will kick in here 
    } 
}; 

而且,最後,指針專業化:

template<typename T> 
struct convert_v8value_to_cpp<T*> { // no need for enable_if 
    static T* do_it(const CefRefPtr<CefV8Value> &value) 
    { 
     if (!value->IsObject()) 
      return nullptr; // don't use NULL in C++ 

     CefRefPtr<CefV8Value> pTypeId = value->GetValue("__v8bind_typeid__"); 
     if (!pTypeId || !pTypeId->IsString()) 
      return nullptr; 

     CefRefPtr<CefV8Value> pPointerVal = value->GetValue("__v8bind_ptr__"); 
     if (!pPointerVal || !pPointerVal->IsInt()) 
      return nullptr; 

     if (pTypeId->GetStringValue().ToString() == typeid(T).name()) 
      return (T*)pPointerVal->GetIntValue(); 

     return nullptr; 
    } 
}; 

這些在現實函數模板現在使用:

template <typename T> 
T convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value) 
{ return convert_v8value_to_cpp_helper<T>::do_it(value); } 

沒有更多的不確定性,因爲只有一個函數模板(但是如果模板偏特訂貨未能找到一個最好的可能有歧義匹配,但這不應該是這種情況)。

2

mmutz解釋爲什麼它的曖昧(你有三個重載採取完全相同的參數類型,可以全部匹配convert_v8value_to_cpp<WrapClass*>

編輯:我看到mmutz現在也增加了一個解決方案,類似下面挖掘。我發現讀取它的全部內容要容易得多,這些宏不會讓我更容易閱讀,甚至不會節省很多輸入。

如果你想部分,專門就需要使用類模板:

template<typename T> 
struct Converter; 

template<> 
struct Converter<bool> 
{ 
    typedef bool result_type; 
    static result_type convert(const CefRefPtr<CefV8Value> &value) 
    { ... } 
}; 

template<typename T> 
struct Converter<std::vector<T>> 
{ 
    typedef std::vector<T> result_type; 
    static result_type convert(const CefRefPtr<CefV8Value> &value) 
    { ... } 
}; 

template<typename T> 
struct Converter<T*> 
{ 
    typedef T* result_type; 
    static result_type convert(const CefRefPtr<CefV8Value> &value) 
    { ... } 
}; 

template<typename T> 
typename Converter<T>::result_type 
convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value) 
{ return Converter<T>::convert(value); } 
+0

謝謝Jonathon。這兩個答案正是我想知道的,我在這個過程中學到了。然而,正如馬克·穆茨首先回答我必須接受他的。 – Saul

+1

無論如何他的回答是更完整的! :)很高興它幫助你理解 –