2015-03-14 2 views
0

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

Во-первых, я пробовал шаблон этой функции. Тем не менее, мне пришлось бы также шаблонировать чистую виртуальную базовую функцию (в показанном коде BBInterface :: Go) - видимо, это невозможно.

Шаблон посетителя звучал так, как будто он может быть применим, но, боюсь, я просто не понимаю. Кроме того, я не понимаю, могу ли я хранить старую библиотеку в черном ящике, или если статическую библиотеку пришлось бы перекомпилировать и связать с новым набором «посетителей» в любое время, когда кто-то добавит возможный тип данных.

Теперь я пытаюсь создать шаблонную структуру внутри функции, которая затем reinterpret_cast-ed эквивалентна структуре (надеюсь)?). См. Ниже

Это, похоже, работает для двух входов (A и B). Это нормально, но я в идеале хотел бы использовать вариативные шаблоны, чтобы иметь потенциально много входов. Это на мой взгляд. Если бы кто-нибудь мог с этим справиться, это было бы здорово.

Итак, есть ли более элегантный способ поддерживать расширяемый функциональный интерфейс (BBContainer :: Do в коде ниже)? Есть ли способ избежать reinterpret_cast? Могу ли я распространить это на более чем два шаблонных аргумента? Есть ли способ проверить успех reinterpret_cast, например dynamic_cast?

#include <iostream> 
#include <vector> 
#include <map> 
#include <memory> 
using namespace std; 

static const double values[] = {0., 1., 2., 3., 4., 5., 6. }; 

// ------ ASSUME THIS BLACK BOX AREA IS IN A STATIC LIBRARY THE USER CAN NOT MODIFY -------// 
struct BBPacket {}; 

class BBInterface 
{ 
public: 
    virtual void Go(BBPacket&) = 0; 
}; 

class BBContainer 
{ 
public: 
    void Add(const string aName, std::unique_ptr<BBInterface>&& aThing) 
    { 
     BBMap[aName] = std::move(aThing); 
    } 

    template <typename A, typename B> 
    void Do(const std::string& aName, A& aVal, const B& aIndex) 
    { 
     struct NewPacket : public BBPacket 
     { 
      NewPacket(A& aVal, const B& aIndex) : mVal(aVal), mIndex(aIndex) {} 
      A& mVal; 
      const B& mIndex; 
     }; 
     NewPacket temp(aVal, aIndex); 
     this->Do(aName, temp); 

    } 

    void Do(const string& aName, BBPacket& aPacket) 
    { 
     BBMap[aName]->Go(aPacket); 
    } 

private: 
    map<std::string, unique_ptr<BBInterface>> BBMap; 
}; 


// ----- The user area is written by the user, and should not be included in the blackbox project! --------- 
struct USingleValuePacket 
{ 
    double& mVal; 
    const int& mIndex; 
}; 

struct UVectorValuePacket 
{ 
    vector<double>& mVals; 
    const vector<int>& mIndices; 
}; 


class USingleExtractor : public BBInterface 
{ 
    virtual void Go(BBPacket& aPacket) 
    { 
     USingleValuePacket& danger = reinterpret_cast<USingleValuePacket&>(aPacket); 
     fprintf(stdout, "The current single value is %1.1f\n", danger.mVal); 
     danger.mVal = values[danger.mIndex]; 
    } 
}; 

class UVectorExtractor : public BBInterface 
{ 
    virtual void Go(BBPacket& aPacket) 
    { 
     UVectorValuePacket& danger = reinterpret_cast<UVectorValuePacket&>(aPacket); 
     for (int i = 0; i < danger.mVals.size(); ++i) 
     { 
      fprintf(stdout, "The current vector value %i is %1.1f\n",i, danger.mVals[i]); 
      danger.mVals[i] = values[danger.mIndices[i]]; 
     } 
    } 
}; 


int main() 
{ 
    BBContainer a; 
    a.Add("f", std::unique_ptr<USingleExtractor>(new USingleExtractor)); 
    a.Add("g", std::unique_ptr<UVectorExtractor>(new UVectorExtractor)); 
    double val = 0.; 
    int index = 4; 
    a.Do("f", val, index); 
    fprintf(stdout, "Expected value is 4.0 and I get %1.1f\n", val); 

    std::vector<double> valVec(3); 
    std::vector<int> indexVec; indexVec.push_back(0); indexVec.push_back(2); indexVec.push_back(5); 
    a.Do("g", valVec, indexVec); 
    fprintf(stdout, "Expected value for index 0 is 0.0 and I get %1.1f\n", valVec[0]); 
    fprintf(stdout, "Expected value for index 1 is 2.0 and I get %1.1f\n", valVec[1]); 
    fprintf(stdout, "Expected value for index 2 is 5.0 and I get %1.1f\n", valVec[2]); 

// a.Do("g", val, index); // This will go into UVectorExtractor with USingleValuePacket data - Not compatible! 

    return 0; 
} 

EDIT:

Я хочу BBContainer :: У иметь гибкую подпись (в этом примере я использую (строка, двойной &, Const INT &) и (строка, вектор &, Const вектор &), но у меня может быть много других). В то же время я не хочу изменять BBInterface (например, с Go (double &, const int), Go (вектор &, const vector &) и т. Д.). Я могу предположить, что производный класс BBInterface знает, какие данные требует его конкретная реализация Go. Итак, как мне перенаправить общие данные из BBContainer :: Do на производные классы BBInterface, когда он имеет доступ только к базовому классу BBInterface, который не разрешен для специализирования? И существует ли более безопасный тип, чем создание шаблонной структуры в базовом классе BBInterface и использование reinterpret_cast в его производных классах?

+0

Каковы ваши фактические * требования *? Мне сложно понять, какую именно функциональность вы хотите предоставить от своей конкретной попытки реализовать.Я думаю, на ваш вопрос должен быть дан ответ на более высоком уровне проектирования. определить, какой должен быть полиморфизм времени выполнения, что должен быть полиморфизм времени компиляции, и какой синтаксический сахар предложить, а не форму, заменить эту часть реализации чем-то другим ». – Hurkyl

+0

@Hurkyl Я попытаюсь объяснить это лучше в редактировании. – DSM

+0

reinterpret_cast ошибочен почти в каждом случае, когда люди все равно хотят его использовать. –

ответ

0

Как указал Хуркил, я должен просто сделать пакет. Кажется, что это достаточно хорошо, и вспомогательная функция для поддержания чистоты интерфейса Do и dynamic_cast вместо reinterpret_cast. Я все еще работаю над вариационными шаблонами для пакетов переменной длины.

Новый пакет:

template<typename A, typename B> 
struct UPacket : public BBPacket 
{ 
    UPacket(A& aVal, const B& aIndex) : mVal(aVal), mIndex(aIndex) {} 
    A& mVal; 
    const B& mIndex; 
}; 

Вспомогательная функция:

template <typename A, typename B> 
void Do(BBContainer& a, const string& aName, A& aVal, const B& aIndex) 
{ 
    a.Do(aName, UPacket<A, B>(aVal, aIndex)); 
} 

Использование:

... 
double val = 0.; 
int index = 4; 
Do(a,"f", val, index); 
... 
std::vector<double> valVec(3); 
std::vector<int> indexVec; indexVec.push_back(0); indexVec.push_back(2); indexVec.push_back(5); 
Do(a, "g", valVec, indexVec); 
Смежные вопросы