Как преобразовать объект среды выполнения C++/CX в собственный тип указателя C?

Я делаю оболочку времени выполнения C++/CX, и мне нужно передать указатель объекта C++/CX на собственный C. Как мне это сделать и преобразовать собственный указатель обратно в тип ссылки на объект C++/CX?

void XClassA::do(XClass ^ B)
{
    void * ptr = (void*)(B);   // how to convert it?
}

Кроме того, C++/CX использует подсчет ссылок, если я привожу ссылку на объект к собственному указателю, как мне управлять жизненным циклом указателя?


обновление (запрос от @Hans Passant)

Предыстория вопроса,

Родной C

Я пытаюсь использовать библиотеку C++/CX wrap Native C (не C++) как Windows Runtime Component. Native c имеет много функций обратного вызова, которые объявлены следующим образом:

Например,

//declare in native c
typedef int (GetData*)(void *, char* arg1, size_t arg2);

void * — это указатель на экземпляр объекта.
и обратный вызов будет выполняться в машинном коде c во время выполнения.
Мы ожидаем, что Application(C#/C++CX ...) реализует этот метод.

Оболочка WinRT (C++/CX)

моя идея заключается в следующем,

(1) Предоставьте interface приложению

// XRtWrapperNamespace
public interface class XWinRtDataWrapper
{
    //declare in base class
    void getData(IVector<byte> ^ data);
}

чтобы позволить приложению реализовать функцию. Поскольку я не могу экспортировать собственный тип данных, я предоставляю IVector для получения данных из приложения.

(2) Объявите глобальную функцию обратного вызова для преобразования IVector<byte>^ в собственный тип данных char *, как показано ниже:

// when Native C executes callback function, 
// it will forward in the method in C++/CX. 
// The method calls the implementation method via object pointer.
// (And here is my my question)   
void XRtWrapperNamespace::callbackWrapper(void * ptr, char *, int length)
{ 
    // create Vector to save "out" data
    auto data = ref new Vector<byte>();
    // I expect I could call the implementation from Application.               
    ptr->getData(data);   // bad example. 

    // convert IVector data to char * 
    // ...
}

Мой вопрос

Как сохранить ссылку на объект Windows на собственный C? Это выглядит невозможным, но есть ли какое-нибудь решение для этого?

Приложение (пример)

 //Application
 public ref class XAppData: public XWinRtDataWrapper
 {
 public:
    virtual void getData(IVector<byte> ^ data) 
    {
      //implementation here
    }
 }

person CCC    schedule 31.12.2015    source источник
comment
Это довольно бессмысленный вопрос, вы никогда не сможете получить указатель на объект. Он живет в совершенно другой среде выполнения, может быть реализован на Javascript или языке .NET. Вы всегда работаете только с указателями интерфейса. Каждый объект WinRT реализует IInspectable, поэтому вы можете получить указатель на этот объект и привести его к типу void*, если вам действительно нужно. Но рано или поздно вам придется вернуться и вызвать Release(), так что в этом нет особого смысла. Вы не сможете получить хороший ответ, если не объясните, почему, по вашему мнению, вам нужно это сделать.   -  person Hans Passant    schedule 31.12.2015


Ответы (1)


Вы не на правильном пути. Я предполагаю, что вы #include заголовок c в свой компонент:

extern "C" {
#include "native.h"
}

И этот заголовок содержит:

typedef int (* GetData)(void* buffer, int buflen);
void initialize(GetData callback);

Где функция initialize() должна быть вызвана для инициализации кода C, установив указатель функции обратного вызова. И что вы хотите, чтобы клиентский код напрямую записывался в буфер, выделенный размер которого равен buflen. Некоторая индикация ошибки была бы полезна, а также позволить клиентскому коду указать, сколько байтов он фактически записал в буфер. Таким образом, int возвращает значение.


Эквивалентом указателей функций в WinRT являются делегаты. Таким образом, вы захотите объявить тот, который соответствует вашему указателю функции C по функциональности. В вашем файле .cpp напишите:

using namespace Platform;

namespace YourNamespace {
    public delegate int GetDataDelegate(WriteOnlyArray<byte>^ buffer);
    // More here...
}

Существует два основных способа разрешить клиентскому коду использовать делегат. Вы можете добавить метод, который позволяет клиенту установить делегата, аналогично тому, как работает initialize(). Или вы можете поднять событие, тем более WinRT-ориентированным способом. Я буду использовать событие. Обратите внимание, что создание экземпляров является проблемой, их не прилично отображать из нескольких объектов компонентов в один указатель функции C. Я приукрашу это, объявив событие static. Написание объявления ref class:

public ref class MyComponent sealed
{
public:
    MyComponent();
    static event GetDataDelegate^ GetData;
private:
    static int GetDataImpl(void* buffer, int buflen);
};

Конструктор класса должен инициализировать код C:

MyComponent::MyComponent() {
    initialize(GetDataImpl);
}

И нам нужен небольшой метод адаптера, который заставляет обратный вызов C вызывать событие, чтобы клиентский код мог заполнить буфер:

int MyComponent::GetDataImpl(void* buffer, int buflen) {
    return GetData(ArrayReference<byte>((byte*)buffer, buflen));
}
person Hans Passant    schedule 03.01.2016
comment
Большое спасибо. Из-за особых требований я должен разрешить каждому экземпляру иметь собственную реализацию функции обратного вызова. Это возможно? - person CCC; 03.01.2016
comment
Вам придется изменить свой код C для поддержки какого-либо экземпляра. Возможно, вам нужен дескриптор или простой указатель, в вашем вопросе нет ничего, что подсказывало бы лучший способ сделать это. Хранить такой дескриптор в вашем ref-классе, конечно, очень просто. Нажмите кнопку «Задать вопрос» и используйте тег [c], если вам нужна помощь с кодом C. - person Hans Passant; 03.01.2016
comment
Хорошо, я понимаю, что вы имеете в виду, позвольте мне попробовать реализацию. Спасибо большое. - person CCC; 03.01.2016