2009-11-25 44 views
0

我有一些代码,我不得不写一些代码来代替几千次的函数。这个函数的问题是返回一个指向静态分配缓冲区的指针,这是一个荒谬的问题。我终于能够证明间歇性的高负载错误是由不好的做法造成的。寻找一些重构建议

我正在更换的功能的签名为char * paddandtruncate(char *,int)char * paddandtruncate(float,int)char * paddandtruncat(int,int)。每个函数都返回一个指向静态分配的缓冲区的指针,该缓冲区在后续调用时被覆盖

我有三个常量之一

  1. 代码必须是可更换的与主叫方没有任何影响。
  2. 很少有时间来解决这个问题。
  3. 可接受的性能。

我想要一些风格和可能的重构想法的意见。

该系统基于用空格填充的固定宽度字段,并且具有一些架构问题。由于该项目的规模大约为1,000,000行,因此这些地址无法解决。

我最初计划在创建后允许更改数据,但认为不可变对象提供了更安全的解决方案。

using namespace std; 
class SYSTEM_DECLSPEC CoreString 
{ 
private: 
    friend ostream & operator<<(ostream &os,CoreString &cs); 

    stringstream m_SS   ; 
    float   m_FltData  ; 
    long   m_lngData  ; 
    long   m_Width  ; 
    string   m_strData  ; 
    string     m_FormatedData; 
    bool   m_Formated ; 
    stringstream SS   ; 


public: 

    CoreString(const string &InStr,long Width): 
      m_Formated(false), 
      m_Width(Width), 
      m_strData(InStr) 
      { 
        long OldFlags = SS.flags(); 
        SS.fill(' '); 
        SS.width(Width); 
        SS.flags(ios::left); 
        SS<<InStr; 
        m_FormatedData = SS.str(); 
      } 

    CoreString(long longData , long Width): 
      m_Formated(false), 
      m_Width(Width), 
      m_lngData(longData) 
      { 
        long OldFlags = SS.flags(); 
        SS.fill('0'); 
        SS.precision(0); 
        SS.width(Width); 
        SS.flags(ios::right); 
        SS<<longData; 
        m_FormatedData = SS.str(); 
      } 

    CoreString(float FltData, long width,long lPerprecision): 
      m_Formated(false), 
      m_Width(width), 
      m_FltData(FltData) 
      { 
        long OldFlags = SS.flags(); 
        SS.fill('0'); 
        SS.precision(lPerprecision); 
        SS.width(width); 
        SS.flags(ios::right); 
        SS<<FltData; 
        m_FormatedData = SS.str(); 
      } 

    CoreString(const string &InStr): 
      m_Formated(false), 
      m_strData(InStr) 
      { 
        long OldFlags = SS.flags(); 
        SS.fill(' '); 
        SS.width(32); 
        SS.flags(ios::left); 
        SS<<InStr; 
        m_FormatedData = SS.str(); 
      } 
public: 
    operator const char *() {return m_FormatedData.c_str();} 
    operator const string&() const {return m_FormatedData;} 
    const string& str() const ; 

}; 

const string& CoreString::str() const 
{ 
    return m_FormatedData; 
} 

ostream & operator<<(ostream &os,CoreString &cs) 
{ 
    os<< cs.m_Formated; 
    return os; 
} 
+1

对不起,有什么问题吗?你已经张贴你做了什么,以及大量的代码,但没有实际的问题 – 2009-11-25 17:28:45

+0

我想,如果给任何人能够想到一个更好的解决方案的限制,我想知道。 – rerun 2009-11-25 18:12:06

回答

2

如果你确实的意思是“对呼叫者没有影响”,你的选择是非常有限的。你不能返回任何需要被调用者释放的东西。

如果冒着将另一个不好的解决方案替换为另一个解决方案的风险,最快最容易的解决方案可能是这样的:不是使用单个静态缓冲区,而是使用它们的池并在每次调用函数时通过它们旋转。确保选择缓冲区的代码是线程安全的。

+0

没有影响我的意思是尽量不用每次打电话都要评估大量文本。实际上,我们正在通过一组缓冲区进行轮换,并且在过去的几年中,我们将增加缓冲区的数量,但这是每个新开发人员都需要发现的炸弹。 – rerun 2009-11-25 17:45:42

+0

我想我理解这个问题 - 随着摩尔定律提供更快的计算机,缓冲区的数量需要增加。肯定的陷阱。 – 2009-11-25 18:03:58

1

你发布的代码有一个巨大的问题 - 如果调用者将返回值赋给const char *,编译器将进行无提示转换并销毁临时的CoreString对象。现在你的指针将会失效。

+0

好抓我懒。我将不得不返回一个字符串作为副本,但我认为性能仍然可以接受。 – rerun 2009-11-25 18:24:34

+0

这是一个很好的想法 - 提供自动转换可最大限度地减少客户端代码更改。不过,自动转换充满危险。 – 2009-11-25 18:37:20

0

“间歇性高负载错误”是由竞争条件引起的,其中一个线程在另一个线程完成使用之前在静态缓冲区上践踏,对吗?

因此,切换到每个线程使用输出缓冲区,使用您的平台提供的任何线程本地存储机制(Windows,我在想)。

没有同步争用,线程之间没有干扰,并根据您对当前实现旋转缓冲区的说法,几乎可以肯定调用代码根本不需要改变。如果当前的实现使用多个缓冲区,它不能依赖每次使用的相同缓冲区。

我可能不会从头开始设计API,但它实现了您当前的API而不会以显着方式更改它,或者影响性能。

2

听起来像系统是线程的,对吧?如果仅仅是在您仍然使用之前的输出时再次调用其中一个函数是不安全的问题,则它应该每次都以相同的方式运行。

大多数编译器有办法来标记变量为“线程本地数据”使之具有取决于哪个线程访问它不同的地址。在GCC是__thread,在VC++中的__declspec(thread)

如果您需要能够从同一个线程多次调用这些功能,而无需重写的结果,我看不到任何完整的解决方案,但迫使调用者释放的结果。您可以使用混合方法,其中每个线程都有固定数量的缓冲区,因此无论其他线程在做什么,呼叫者都可以在不覆盖以前的结果的情况下组成N个调用。

1

我不知道调用者将如何使用它,但使用new将缓冲区分配到auto_ptr<>可能会起作用。它可能满足标准1(我没有看到使用的代码,我不知道),并可能是一个非常快的修复。最大的问题是它会使用大量的动态内存,这会让速度变慢。有些事情你可以做,使用新的布局等,但这可能不是很快就可以编码。

如果您不能使用动态存储,你只限于非动态存储,而且确实没有什么可以做,不使用缓冲区或线程局部缓冲或类似的东西的旋转池。

+0

可能不是。 'auto_ptr的<>'不处理阵列,并且如果变化表明大的代码是可以接受的,人们可能只切换到使用'的std :: string'和由返回值之一。 – UncleBens 2009-11-25 19:19:56

+0

对,忘了这是一个数组 - 而'std :: string'可能会更好。 – 2009-11-25 20:13:50