有没有办法自动类型转换 void 指针?

Is there a way to automatically typecast void pointers?

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

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*(字符串)。

除了手动查找每个函数的所有数据字段,然后按类型手动筛选并制作具有不同 return 类型的 get 函数外,还有其他方法吗?

更新:

指定我要实现的目标:

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

例如:

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)
    }
    (...)
};

下面这行语法不正确。

auto n_tracks = (auto)(*ptr);

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

您可以将 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 时知道字段的类型,我不确定您是否属于这种情况。不过请考虑一下:如果您不知道该类型应该是什么,您怎么能期望您的编译器能够推断出它呢?

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

假设我们已经实现了该功能,并且: 1) 如果我们调用函数get("wbc"),它是return int; 2) 如果我们调用函数get("delay"),它return 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
}

编译器需要所有信息就位并进行编译,以便它可以向CPU.

提供所有例程(或ASM)

你可能会想到模板

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 *****?
}

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

不要那样做。它是有害的,会破坏类型的安全;悬挂指针也没什么好。

只需让您的变量 public:您公开它们的方式使 private 基本上毫无用处。如果您需要限制对这些的访问,请使用代理对象 and/or friend.

你可以用continuation passing style来解决这个问题。

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{} );

这将调用类型为 wbcsome_operationoperator()

您可以施展魔法,使语法看起来像这样:

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

或者在 C++14 中:

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

我们使用 operator->* 进行一些链接。但这越来越花哨了。


请注意,您应该采取 F&& 并执行上面的 std::forward<F>(f)(wbc);,但这并不重要,而且会混淆问题。


您也可以使用 boost::variant 之类的方法将问题分成两部分,它可以很好地将 "get the data" 与 "process each type of the data" 部分分开。

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

示例:

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

此代码现在足够智能,可以根据从配置文件中读取的类型执行不同的操作。