检查POD变量的变化

时间:2014-11-15 08:31:42

标签: c++ templates malloc memcpy

我正在寻找一种有效的方法来检查POD变量是否在两个周期之间被改变。我已经提出了这个解决方案:

class Foo {
public:
    template<typename T>
    bool isChanged(T& entry);
    void endCycle();
private:
     std::map<void*,size_t> entryMap;  // <Address orig.,Size>
     std::map<void*,void*>oldVals;     // <Address orig., Address cpy.> 
};

template<typename T> bool Foo::isChanged(T& entry)
{
    entryMap[&entry] = sizeof(T);
    if(oldVals[&entry] == NULL)
        return false;
    if(memcmp(&entry, oldVals[&entry], entryMap[&entry]))
        return true;
    else
        return false;
}

void Foo::endCycle()
{   
    // Copy all the bytes to save them for the next cycle
    for(  std::map<void*,size_t>::iterator entryIt = entryMap.begin();
          entryIt != entryMap.end();
          ++entryIt)
    {
        if(oldVals[entryIt->first] == NULL)
            oldVals[entryIt->first] = malloc(entryIt->second);
        memcpy(oldVals[entryIt->first], entryIt->first, entryIt->second);
    }
}

现在我可以像这样使用它:

Foo gBar;
void aFunction()
{
int ar;
char ba[3][3];
// Some code where ar and ba are filled
if(gBar.isChanged(ar))
    // Do Something
if(gBar.isChanged(ba))
    // Do Something
gBar.endCycle();
}

这是一种有效的方法吗?我的目标是一种在各种循环调用函数中非常容易使用的方法。我从代码中清除了所有init和free逻辑。有什么建议?我特别不喜欢oldshool malloc,memcpy和memcmp,但我不知道如何做到这一点。

编辑:根据红色警报建议找到一个好的解决方案。

2 个答案:

答案 0 :(得分:1)

我认为你可以在这里更有效地使用模板。

template <typename T>
class Foo
{
public:
    static std::map<T*, T> values;

    static bool isChanged(T& entry)
    {    
        auto it = values.find(&entry);

        if(it == values.end())
        {
            values[&entry] = entry;
        }
        else if(entry != it->second)
        {    
            it->second = entry;
            return true;
        }

        return false;
    }
};


template <typename T>
std::map<T*, T> Foo<T>::values;

int main() {

    int ar = 3;
    cout << Foo<int>::isChanged(ar) << endl; // 0

    ar = 4;

    cout << Foo<int>::isChanged(ar) << endl; // 1

    for(auto& value : Foo<int>::values)
        cout << value.second << endl; // 4

    return 0;
}

通过这种方式,您可以获得每种类型一个map,并且您不必担心会无意中弄乱别名。您需要定义operator !=并为您的类型设置一个工作副本构造函数,但这比盲目使用memcmpmemcpy要好得多。

如果你需要比较那些数组,你还可以为数组做进一步的模板特化(将会有更多的代码,但没有什么非常复杂的)

编辑:为了让您入门,这就是您的模板签名应如下所示:

template<class T, size_t N> bool isChanged(T(&entry)[N]); //will be called for stack allocated arrays

或者您可以使用char *来为所有值设置别名。这将允许您为所有内容使用单个地图(就像之前一样,但这没有memcpy / memcmp)。它只适用于POD。我们可以在覆盖缓冲区时手动调用析构函数,但由于在类的析构函数中没有好的方法可以做到这一点,所以最好不要完全忽略堆分配的数据。

class Foo
{
    std::map<char**, char*> values;

public:
    ~Foo()
    {
        for(auto& value : values)
        {
            delete[] value.second;
        }
    }

    template<typename T> bool isChanged(T& entry)
    {
        char** addr = reinterpret_cast<char**>(&entry);
        auto it = values.find(addr);

        if(it == values.end())
        {
            alignas(T) char* oldBuf = new char[sizeof(T)];
            T* oldEntry = new(oldBuf) T;
            *oldEntry = entry;
            values[addr] = oldBuf;
        }
        else if(entry != *(reinterpret_cast<T*>(it->second)))
        {
            T* oldEntry = new(it->second) T;
            *oldEntry = entry;
            return true;
        }

        return false;
    }
};

答案 1 :(得分:0)

经过几个小时,我觉得我找到了一个很好的解决方案。通话保持简单,没有演员阵容。它比使用memcopy的C风格版本复杂得多,但我认为它更好,并且还有一个好处,它适用于复杂的数据而不仅仅是POD。

class Manager
{
public:
    ~Manager()
    {
        funcPtrs.clear();
    }
    void adFnc(void(*function)())
    {
        funcPtrs.push_back(function);
    }
    void runAll()
    {
        for(auto& val : funcPtrs)
            val();
    }
private:
    std::vector<void (*)()> funcPtrs;
};

Manager gAllClearManager;

template<typename T>
class Data
{
public:
    Data()
    {
        gAllClearManager.adFnc(clearValues);
    }
    static void clearValues()
    {
        values.clear();
    }
    static std::map<T*,std::vector<T>>& getValues() { return values; }
private:
    static std::map<T*,std::vector<T>> values;
};

template <typename T>
static bool isChanged(T& entry)
{
    const static Data<T>* dataP = new  Data<T>();
    static std::map<T*,std::vector<T>>&  values = dataP->getValues();

    auto it = values.find(&entry);

    if(it == values.end())
    {
        values[&entry].push_back(entry);
    }
    else if(entry != it->second[0])
    {    
        it->second[0] = entry;
        return true;
    }
    return false;
}

template<typename T, size_t N>
bool isChanged(T (&entry)[N])
{
    const static Data<T>* dataP = new  Data<T>();
    static std::map<T*,std::vector<T>>&  values = dataP->getValues();

    auto it = values.find(entry);

    if( it == values.end())
    {
        for(int i = 0; i < N ; ++i )
            values[entry].push_back(entry[i]);
        return false;
    }
    else
    {
        for(int i = 0; i < N ; ++i )
        {
            if(it->second[i] != entry[i])
            {
                for(int j = 0; j < N ; ++j )
                {
                    it->second[j] = entry[j];
                }
                return true;
            }
        }
    }
    return false;
}

template<typename T>
std::map<T*, std::vector<T>> Data<T>::values;

现在我可以像以下一样使用它:

int main() {
    int ar;
    std::string ba[6];
    if(isChange(ar))
        // Do something
    if(isChange(ba)) 
        // Do something
}

我的第一个模板终于工作了! :)再次感谢红色警报。