Cómo: Extender la biblioteca de cálculo de referencias

En este tema se explica cómo ampliar la biblioteca de serialización para proporcionar más conversiones entre tipos de datos. Los usuarios pueden ampliar la biblioteca de serialización para cualquier conversión de datos que no sea compatible actualmente con la biblioteca.

Puede ampliar la biblioteca de serialización de una de estas dos maneras: con o sin una clase marshal_context. Revise el tema Información general sobre serialización en C++ para determinar si una nueva conversión requiere un contexto.

En ambos casos, primero se crea un archivo para las nuevas conversiones de serialización. Lo hace para conservar la integridad de los archivos de biblioteca de serialización estándar. Si desea migrar un proyecto a otro equipo o a otro programador, debe copiar el nuevo archivo de serialización junto con el resto del proyecto. De esta manera, se garantiza que el usuario que recibe el proyecto reciba las nuevas conversiones y no tendrá que modificar ningún archivo de biblioteca.

Para ampliar la biblioteca de serialización con una conversión que no requiere un contexto

  1. Cree un archivo para almacenar las nuevas funciones de serialización, por ejemplo, MyMarshal.h.

  2. Incluya uno o varios de los archivos de biblioteca de serialización:

    • marshal.h para los tipos base.

    • marshal_windows.h para tipos de datos de Windows.

    • marshal_cppstd.h para los tipos de datos de la biblioteca estándar de C++.

    • marshal_atl.h para los tipos de datos de ATL.

  3. Use el código al final de estos pasos para escribir la función de conversión. En este código, TO es el tipo al que se va a convertir, FROM es el tipo desde el que se va a convertir y from es el parámetro al que se va a convertir.

  4. Reemplace el comentario sobre la lógica de conversión por código para convertir el parámetro from en un objeto de tipo TO y devolver el objeto convertido.

namespace msclr {
   namespace interop {
      template<>
      inline TO marshal_as<TO, FROM> (const FROM& from) {
         // Insert conversion logic here, and return a TO parameter.
      }
   }
}

Para ampliar la biblioteca de serialización con una conversión que requiere un contexto

  1. Cree un archivo para almacenar las nuevas funciones de serialización, por ejemplo, MyMarshal.h.

  2. Incluya uno o varios de los archivos de biblioteca de serialización:

    • marshal.h para los tipos base.

    • marshal_windows.h para tipos de datos de Windows.

    • marshal_cppstd.h para los tipos de datos de la biblioteca estándar de C++.

    • marshal_atl.h para los tipos de datos de ATL.

  3. Use el código al final de estos pasos para escribir la función de conversión. En este código, TO es el tipo al que se va a convertir, FROM es el tipo desde el que se va a convertir, toObject es un puntero en el que se va a almacenar el resultado y fromObject es el parámetro al que se va a convertir.

  4. Reemplace el comentario sobre la inicialización con código para inicializar toPtr al valor vacío apropiado. Por ejemplo, si es un puntero, establézcalo en NULL.

  5. Reemplace el comentario sobre la lógica de conversión por código para convertir el parámetro from en un objeto de tipo TO. Este objeto convertido se almacenará en toPtr.

  6. Reemplace el comentario sobre la configuración toObject por el código para establecer toObject en el objeto convertido.

  7. Reemplace el comentario sobre la limpieza de recursos nativos por código para liberar cualquier memoria asignada por toPtr. Si toPtr asignó memoria mediantenew, use delete para liberar la memoria.

namespace msclr {
   namespace interop {
      template<>
      ref class context_node<TO, FROM> : public context_node_base
      {
      private:
         TO toPtr;
      public:
         context_node(TO& toObject, FROM fromObject)
         {
            // (Step 4) Initialize toPtr to the appropriate empty value.
            // (Step 5) Insert conversion logic here.
            // (Step 6) Set toObject to the converted parameter.
         }
         ~context_node()
         {
            this->!context_node();
         }
      protected:
         !context_node()
         {
            // (Step 7) Clean up native resources.
         }
      };
   }
}

Ejemplo: extensión de la biblioteca de serialización

En el ejemplo siguiente se extiende la biblioteca de serialización con una conversión que no requiere un contexto. En este ejemplo, el código convierte la información de empleado de un tipo de datos nativo en un tipo de datos administrado.

// MyMarshalNoContext.cpp
// compile with: /clr
#include <msclr/marshal.h>

value struct ManagedEmp {
   System::String^ name;
   System::String^ address;
   int zipCode;
};

struct NativeEmp {
   char* name;
   char* address;
   int zipCode;
};

namespace msclr {
   namespace interop {
      template<>
      inline ManagedEmp^ marshal_as<ManagedEmp^, NativeEmp> (const NativeEmp& from) {
         ManagedEmp^ toValue = gcnew ManagedEmp;
         toValue->name = marshal_as<System::String^>(from.name);
         toValue->address = marshal_as<System::String^>(from.address);
         toValue->zipCode = from.zipCode;
         return toValue;
      }
   }
}

using namespace System;
using namespace msclr::interop;

int main() {
   NativeEmp employee;

   employee.name = "Jeff Smith";
   employee.address = "123 Main Street";
   employee.zipCode = 98111;

   ManagedEmp^ result = marshal_as<ManagedEmp^>(employee);

   Console::WriteLine("Managed name: {0}", result->name);
   Console::WriteLine("Managed address: {0}", result->address);
   Console::WriteLine("Managed zip code: {0}", result->zipCode);

   return 0;
}

En el ejemplo anterior, la función marshal_as devuelve un identificador a los datos convertidos. Esto se hizo para evitar la creación de una copia adicional de los datos. Devolver la variable directamente tendría un costo de rendimiento innecesario asociado.

Managed name: Jeff Smith
Managed address: 123 Main Street
Managed zip code: 98111

Ejemplo: conversión de información de empleado

En el ejemplo siguiente se convierte la información de los empleados de un tipo de datos administrado a un tipo de datos nativo. Esta conversión requiere un contexto de serialización.

// MyMarshalContext.cpp
// compile with: /clr
#include <stdlib.h>
#include <string.h>
#include <msclr/marshal.h>

value struct ManagedEmp {
   System::String^ name;
   System::String^ address;
   int zipCode;
};

struct NativeEmp {
   const char* name;
   const char* address;
   int zipCode;
};

namespace msclr {
   namespace interop {
      template<>
      ref class context_node<NativeEmp*, ManagedEmp^> : public context_node_base
      {
      private:
         NativeEmp* toPtr;
         marshal_context context;
      public:
         context_node(NativeEmp*& toObject, ManagedEmp^ fromObject)
         {
            // Conversion logic starts here
            toPtr = NULL;

            const char* nativeName;
            const char* nativeAddress;

            // Convert the name from String^ to const char*.
            System::String^ tempValue = fromObject->name;
            nativeName = context.marshal_as<const char*>(tempValue);

            // Convert the address from String^ to const char*.
            tempValue = fromObject->address;
            nativeAddress = context.marshal_as<const char*>(tempValue);

            toPtr = new NativeEmp();
            toPtr->name = nativeName;
            toPtr->address = nativeAddress;
            toPtr->zipCode = fromObject->zipCode;

            toObject = toPtr;
         }
         ~context_node()
         {
            this->!context_node();
         }
      protected:
         !context_node()
         {
            // When the context is deleted, it will free the memory
            // allocated for toPtr->name and toPtr->address, so toPtr
            // is the only memory that needs to be freed.
            if (toPtr != NULL) {
               delete toPtr;
               toPtr = NULL;
            }
         }
      };
   }
}

using namespace System;
using namespace msclr::interop;

int main() {
   ManagedEmp^ employee = gcnew ManagedEmp();

   employee->name = gcnew String("Jeff Smith");
   employee->address = gcnew String("123 Main Street");
   employee->zipCode = 98111;

   marshal_context context;
   NativeEmp* result = context.marshal_as<NativeEmp*>(employee);

   if (result != NULL) {
      printf_s("Native name: %s\nNative address: %s\nNative zip code: %d\n",
         result->name, result->address, result->zipCode);
   }

   return 0;
}
Native name: Jeff Smith
Native address: 123 Main Street
Native zip code: 98111

Consulte también

Información general de la serialización en C++