【问题标题】:Is there a way to automatically typecast void pointers?有没有办法自动类型转换 void 指针?
【发布时间】:2015-10-10 17:23:04
【问题描述】:

我目前正在编写一个代码,我想用它从具有大量数据字段的对象中获取数据。我的代码如下所示:

void* get( std :: string field_name )
{
    (...)
    if( field_name == "wbc" )             { return &(this -> wbc); };
    if( field_name == "delay" )           { return &(this -> delay); };
    if( field_name == "ntracks" )         { return &(this -> ntracks); };
    if( field_name == "ntrackFPix" )      { return &(this -> ntrackFPix); };
    if( field_name == "ntrackBPix" )      { return &(this -> ntrackBPix); };
    if( field_name == "ntrackFPixvalid" ) { return &(this -> ntrackFPixvalid); };
    if( field_name == "ntrackBPixvalid" ) { return &(this -> ntrackBPixvalid); };
    (...)
    std :: cerr << "Error: EventData.get() is unable to find the field: " 
                << field_name << ". " << std :: endl;
    exit( -1 );
    return NULL;
}

这就是我调用 get() 函数 (C++11) 的方式:

void* ptr     = this -> event_field -> get( "ntracks" );
auto n_tracks = (auto)(*ptr);

然而,这给了我一个错误信息...... 有没有办法实现我想要的?

我有非常大的结构,其中包含以下类型的字段:int、double、int*(数组)、double*(数组)、char*(字符串)。

除了手动查找每个函数的所有数据字段,按类型手动过滤掉它们并制作具有不同返回类型的get函数之外,还有其他方法吗?

更新:

指定我想要实现的目标:

我知道类型,但因情况而异。有没有可以用来将类型从类传递给函数的解决方案?

例如:

Class A
{
    std :: vector<std :: string> typenames;
    std :: vector<std :: string> identifiers;
};

Class B
{
    (...)
    {
         (I want to get the given field of given type (specified in A) from a massive object with lots of fields( and I don't know before runtime which fields I will need)
    }
    (...)
};

【问题讨论】:

  • 你有一个 void 指针,那么编译器怎么知道它指向的底层类型呢?
  • 你总是可以reinterpret_cast&lt;void*&gt;(...) 函数中的所有内容。不过,我不确定这对auto n_tracks = (auto)(*ptr); 是否有帮助;我想你可能需要再做一个 reinterpret_cast 回到那里的适当类型。
  • 将字段名称隐藏在字符串后面有什么意义?我的意思是,你不能写this -&gt; wbc 代替event_field -&gt; get( "wbc" ) 吗?
  • 如果您告诉我们您最终想要达到的目标可能会有所帮助。即使您可以自动将 void 指针转换为特定类型,您仍然可以对结果变量做什么?您不能以相同的方式处理 intints 数组,因此无论如何您都没有通过这样做获得任何收益。
  • [OT] 您应该将字段名称作为 const string&amp; 传递,因为您不想传递字符串的副本(在堆栈上)和 const 因为您不打算更改字段名称。

标签: c++ c++11 casting return-value auto


【解决方案1】:

您可以将 get 函数包装在一个函数模板中,该模板将值转换为模板参数类型。它会减少冗长,尽管我怀疑您的程序设计中有一个更大的问题使得这些强制转换成为必要。

template<T>
T* get(std::string field) {
  return reinterpret_cast<T*>(this->event_field->get(field));
}

// Used as such
double* ptr =  this->get<double>("field_name");

请注意,这需要您在调用get 时知道该字段的类型,我不确定您的情况。不过请考虑一下:如果您不知道该类型应该是什么,您怎么能期望您的编译器能够推断出它?

【讨论】:

  • 这对于 OP 来说可能是最好的了。不妨把reinterpret_cast&lt;T*&gt;移到event_field-&gt;get成员函数的逻辑里,做成函数模板。
  • @AdamHunyadi 你不能在运行时确定这样的类型(除非RTTI),当你认为需要这样做时,它通常会突出你的设计中的缺陷。
【解决方案2】:

下面这行语法错误。

auto n_tracks = (auto)(*ptr);

编译器无法在编译时推断ptr 的底层类型可能是什么。由于这必须在编译时解决,因此您的方法不是可行的解决方案。你必须想出一个不同的策略来满足你的程序的需要。

【讨论】:

    【解决方案3】:

    你可以用延续传递风格解决这个问题。

    template<class F>
    auto get( std :: string field_name, F f )
    -> typename std::result_of< F(int&) >::type // assuming at least one arg is an int
    {
      (...)
      if( field_name == "wbc" )             { return f(wbc); };
      if( field_name == "delay" )           { return f(delay); };
      if( field_name == "ntracks" )         { return f(ntracks); };
      if( field_name == "ntrackFPix" )      { return f(ntracFPix); };
      if( field_name == "ntrackBPix" )      { return f(ntrackBPix); };
      if( field_name == "ntrackFPixvalid" ) { return f(ntrackFPixvalid); };  
      if( field_name == "ntrackBPixvalid" ) { return f(ntrackBPixvalid); };
      (...)
      std :: cerr << "Error: EventData.get() is unable to find the field: " 
                << field_name << ". " << std :: endl;
      exit( -1 ); // no more need for a return after an exit(-1);
    }
    

    使用看起来像:

    struct some_operation {
      template<class T>
      void operator()(T& t){
        std::cout << t << '\n';
      }
    };
    
    foo.get( "wbc", some_operation{} );
    

    这将调用some_operationoperator() 类型为wbc

    你可以做一些魔术来使语法看起来像:

    foo.get( "wbc" )
    ->* some_operation{}
    

    或在 C++14 中:

    foo.get( "wbc" )
    ->*[&](auto&& val){
      std::cout << val << '\n';
    };
    

    我们使用operator-&gt;* 进行链接。但这越来越花哨了。


    请注意,您应该在上面使用F&amp;&amp;std::forward&lt;F&gt;(f)(wbc);,但这很少有关系,而且会混淆问题。


    您还可以使用boost::variant 之类的方法将问题分成两部分,它将“获取数据”与“处理每种类型的数据”组件很好地分开。

    【讨论】:

      【解决方案4】:

      我认为 Anthony Vallée-Dubois 的结果是最好的答案。原因如下:

      假设我们已经实现了该功能,并且: 1) 如果我们调用函数get("wbc"),它返回int; 2) 如果我们调用函数get("delay"),它返回std::string;

      void valueUser( const std::string& str )
      {
           ***** value = get(str); //What we can input to the place *****??
           value ++; // can it be possible for all return type
           if ( value.at(0) == 'M' );  // can it be possible for all return type
      }
      

      编译器需要所有信息到位并进行编译,以便它可以将所有例程(或 ASM)提供给 CPU。

      你可能会想到模板

      template<>
      void valueUser<int>( const std::string& str )
      {
           int value = get(str); //how we can grantee the return is int?
           value ++;
      }
      
      template<>
      void valueUser<std::string>( const std::string& str )
      {
           std::string value = get(str); //how we can grantee the return is std::string?
           if ( value.at(0) == 'M' );
      }
      
      int main()
      {
          valueUser<*****>(""); // what is *****?
      }
      

      所以这是不可能的,因为您必须在某些地方定义和硬编码数据类型

      【讨论】:

        【解决方案5】:

        不要那样做。有害并破坏类型安全;悬空指针也好不到哪里去。

        只需将变量设置为public:公开它们的方式使private 基本上无用。如果您需要限制对它们的访问,请使用代理对象和/或friend

        【讨论】:

          【解决方案6】:

          解决方案很简单,只需调用 get 函数,然后根据传递的类型做不同的事情:

          例子:

          void* ptr = ... -> get( "ntracks" );
          if( my_object -> interpret_as == "int" )
          {
               callsomefunc( (int*)ptr ); 
          }
          ...
          

          这段代码现在足够聪明,可以根据从配置文件中读取的类型做不同的事情。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-06-16
            • 2021-11-18
            • 2015-02-14
            • 1970-01-01
            • 2021-09-20
            • 1970-01-01
            • 2014-08-30
            相关资源
            最近更新 更多