C ++ Создание API с использованием классов интерфейса, шаблонов и фабрики объектов - PullRequest
0 голосов
/ 06 декабря 2010

Я хотел бы создать API, который предоставляет интерфейс класса для функциональности (файл заголовка) и скрыть реализацию по всем стандартным уважительным причинам.Я планирую использовать фабрику объектов для возврата указателей объектов производных «новых» объектов, которые соответствуют интерфейсу.

Мой базовый класс API варьируется в зависимости от std :: векторы встроенных числовых типов (char, uchar, короткий, ushort, int, uint, float и double).Таким образом, шаблон выглядит как естественная подгонка.Я сделаю шаблон класса интерфейса доступным для пользователей моего API и выведу его из шаблона реализации, который будет скрыт.

Поскольку мой видимый для пользователя шаблон класса - это интерфейсный класс, я бы хотелобъявлять все методы чисто виртуальными, но я понимаю, что могут возникнуть проблемы с выполнением этого требуемого экземпляра шаблона / экспорта в dll, общих объектах и ​​т. д. Поэтому я просто определю их виртуальные и дам им пустые тела методов в базовом классе интерфейса.Где-то вдоль линии мне нужно создать шаблон статического метода фабрики (или шаблонную функцию), который будет создавать объекты производных классов и возвращать указатели на них вызывающей стороне.

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

Вот заголовок концептуальной реализации

#ifndef INTERFACE_H
#define INTERFACE_H

#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

// This interface class is exported from the dll.

template < typename T >
class DLL_API InterfaceClass {
public:
 static InterfaceClass* factoryMethod( );
 virtual ~InterfaceClass ( ) { }

 virtual void someMethod( T aParam ){ };

protected:
 InterfaceClass ( ) { }

private:
 InterfaceClass ( const InterfaceClass & );
 InterfaceClass& operator=( const InterfaceClass & );
};

#endif

Вот класс концептуальной производной реализации

#ifndef IMPLEMENTATION_H
#define IMPLEMENTATION_H

#include <vector>
#include "interface.h"

template < typename T >
class DerivedClass : public InterfaceClass< T > {
public:
 DerivedClass( const T& aDataVector ) : InterfaceClass< T >( ) { /*...*/ }

 virtual ~DerivedClass( ) { /*...*/ }

 virtual void someMethod( T aParam ) { /*...*/ }
private:
 std::vector< T > _dataVector;

};

Примечание. На самом деле я буду использовать TR1 :: shared_ptr вместо необработанных указателей.

Мои вопросы:

1) Где определить статический метод "factoryMethod ()" (creation.h или creation.cpp)?

2) Как выглядит реализация этого метода?

3) Есть ли какие-либо другие проблемы, о которых мне нужно знать, чтобы я или мои пользователи API не получали ошибок во время соединения или во время выполнения?

заранее спасибо!

Ответы [ 2 ]

0 голосов
/ 08 декабря 2010

Я получил его для работы в Visual studio 2008.

Ниже приведены подробности:

interface.h

#ifndef INTERFACE_H
#define INTERFACE_H

#if defined( WIN32 ) || defined ( WIN64 )

#ifdef DLL_EXPORTS
#define DECLSPECIFIER __declspec( dllexport )
#define EXPIMP_TEMPLATE 
#else // DLL_EXPORTS
#define DECLSPECIFIER __declspec( dllimport )
#define EXPIMP_TEMPLATE extern 
#endif // DLL_EXPORTS

#else // defined( WIN32 ) || defined ( WIN64 )
#define DECLSPECIFIER 
#define EXPIMP_TEMPLATE 
#endif // defined( WIN32 ) || defined ( WIN64 )

// This class is exported from the dll.

template < typename T >
class DECLSPECIFIER InterfaceClass {
public:
 static InterfaceClass* factoryMethod( );
 virtual ~InterfaceClass( ) { }

 virtual void someMethod( ) { }

protected:
 InterfaceClass( ) { }

private:
 InterfaceClass( const InterfaceClass& );
 InterfaceClass& operator=( const InterfaceClass& );
};

#if defined( WIN32 ) || defined ( WIN64 ) 
#pragma warning( push )
#pragma warning( disable: 4231 ) // "nonstandard extension used : 'extern'
                                 // ok per [link text][1]
#endif

#include <vector>

EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< char > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< unsigned char > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< short > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< unsigned short > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< int > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< unsigned int > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< float > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< double > >;

#if defined( WIN32 ) || defined ( WIN64 ) 
#pragma warning( pop )
#endif

#endif

производная. Ч

#ifndef DERIVED_H
#define DERIVED_H

#include <iostream>

#include "interface.h"

template < typename T >
class DerivedClass : public InterfaceClass< T > {
public:
 DerivedClass( ) { 
    std::cout << "constructing derived class" << std::endl;

 }

 virtual ~DerivedClass( ) {
    std::cout << "destructing derived class" << std::endl;
 }

 virtual void someMethod( ) {
    std::cout << "hello" << std::endl;
 }
private:
 T _data;
};

#endif

interface.cpp

#include "interface.h"
#include "derived.h"

template < typename T >
DECLSPECIFIER
InterfaceClass<T>* InterfaceClass< T >::factoryMethod( ) {
  return new DerivedClass< T >( );
}

client.cpp

#include <exception>

#include "interface.h"

typedef InterfaceClass < std::vector< int > > IntVectorType;

int main(int argc, char* argv[])
{
 IntVectorType* ptrTest = NULL;
 try {
    ptrTest = IntVectorType::factoryMethod( );
 } 
 catch ( std::bad_alloc& ) {
    return 1;
 }

 ptrTest->someMethod( );

 delete ptrTest;

 return 0;
}

Это может быть нестандартным, но очень полезным.

0 голосов
/ 06 декабря 2010

У вас возникнут проблемы с этим:

1) Шаблоны не могут быть представлены такими библиотеками DLL, как они «заполняются» во время компиляции. То есть, если у вас есть DerivedClass<int>, компилятор фактически заполняет «int» везде, где у вас есть «T», чтобы определить объект. Пытаясь выставить его через DLL, клиентский код (приложение, которое попытается связать ваш объект) может попытаться создать объект DerivedClass<int>, и вы получите ошибку компоновщика, потому что он не существует.

2) Хотя стандарт C ++ технически допускает разделение шаблонных классов на файлы реализации и заголовочные файлы, ни один компилятор не поддерживает эту функцию (так как пытаться реализовать это очень сложно). Это включает в себя компилятор Microsoft C ++. Есть несколько способов обойти это: записать класс inline в файл заголовка (что и делают большинство реализаций STL), или #include файл реализации внизу файла заголовка (который по сути делает то же самое, но не совсем стандартная практика).

...