没有reinterpret_cast的可扩展库接口

时间:2015-03-14 06:54:31

标签: c++ static-libraries reinterpret-cast

我正在构建一个静态库,将用于许多未来的项目。我不想限制此静态库中特定函数的接口,因此应用程序代码可以在数据类型中具有灵活性。该库将保存用户需要的对象的纯虚基类指针。

首先,我尝试模板化这个功能。但是,我必须模拟纯虚拟基本函数(在显示的代码中,BBInterface :: Go) - 显然这是不可能的。

访客模式听起来可能适用,但我担心我不会得到它。我还不明白我是否可以将静态库保持黑盒子,或者静态库是否必须重新编译并与一组新的访问者"任何时候有人添加可能的数据类型。

我现在正在尝试在函数内部创建一个模板化的结构,然后将其重新解释为一个(希望是?)等效结构。见下文

这似乎适用于两个输入(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;
}

编辑:

我希望BBContainer :: Do有一个灵活的签名(在这个例子中我使用(string,double&amp;,const int&amp;)和(string,vector&amp;,const vector&amp;),但我可能还有更多)。同时我不想修改BBInterface(例如使用Go(double&amp;,const int),Go(vector&amp;,const vector&amp;)等)。我可以假设BBInterface的派生类知道它的特定Go实现所需的数据。 那么当它只能访问BBInterface基类时,如何将BBContainer :: Do中的通用数据转发到BBInterface的派生类 - 这是不允许专门的?并且,是否存在比在BBInterface基类中生成模板化结构并在其派生类中使用reinterpret_cast更类型安全的方法?

1 个答案:

答案 0 :(得分:0)

正如Hurkyl指出的那样,我应该制作一个包。使用它和帮助函数来保持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);