【问题标题】:Logging for different data types in C在 C 中记录不同数据类型
【发布时间】:2015-05-11 20:34:12
【问题描述】:

我正在为数据记录类型函数编写一些C 代码。从变量中获取值,执行简单的操作,然后将数据塞入另一个变量中。

这里的关键是容纳如此多不同的数据类型和对数据的操作。我尝试使用指向所有不同变量的 void 指针。然后我为每个调用相同的函数。

看一眼函数。你会看到我为每种不同的数据类型一遍又一遍地重写代码。

    /***********************************************************/
    static void GenData(USHORT data_type,
                         USHORT operation,
                         void *var_p,
                         void *data_p)
    /***********************************************************/
   {
      switch (data_type)
      {
      case (DATA_INT):
        switch (operation) {
        case (OP_ONE_SHOT):
          *(int*)data_p = *(int*)var_p;
          break;
        case (OP_COUNTER):
          *(int*)data_p += 1;
          break;
        case (OP_CURR_TIME):
          *(int*)data_p = (int)sytime;
        case (OP_ELAPSED_TIME):
          *(int*)data_p += delta_tick_time;
          break;
        case (OP_MIN):
          if (*(int*)data_p > *(int*)var_p) {
            *(int*)data_p = *(int*)var_p;
          }
          break;
        case (OP_MAX):
          if (*(int*)data_p < *(int*)var_p) {
            *(int*)data_p = *(int*)var_p;
          }
          break;
        case (OP_ADD_ITSELF):
          *(int*)data_p += *(int*)data_p;
          break;
        default:
          break;
        }
        break;

      case (DATA_BYTE):
        switch (operation) {
        case (OP_ONE_SHOT):
          *(BYTE*)data_p = *(BYTE*)var_p;
          break;
        case (OP_COUNTER):
          *(BYTE*)data_p += 1;
          break;
        ...
        ...

      case (DATA_SHORT):
        ...and so on...
    }

C 中的 void 指针的算术无效。我不确定这是否意味着我不能让这个功能变得更好。有没有办法可以写这个,这样我就不必一遍又一遍地重写“数学”部分?

与往常一样,感谢您花时间和精力提供帮助。

【问题讨论】:

  • 请发布确切的错误消息,复制并粘贴您的编译器错误。
  • 您可以为最终具有相同代码的类型使用模板,但最终,类型不同的原因是它们不同,您的代码也会不同。您是否打算允许 ++ 加倍?你有一个比率类型,它的代码不会不同吗?根据您使用的编译器,您可能可以使用“typeof”来传递一个类型以将其转换为。如果可以组合案例,您也可以在您的 switch 语句中组合它们。但是,您需要类型以便上溢/下溢操作发生在正确的位置。
  • 我认为这里的代码与其说是问题本身,不如说是问题的征兆。您可能应该修改该组件所在软件的架构:为什么它确实需要一堆不同的类型?不过,可能是有原因的。如果是这样,一个好的方法可能是 Dmitry Grigoryev 的答案。您可能还想修改这些指针参数的用途:它们真的是 C 数据类型还是存储格式?如果是后者,这种方法可能是错误的,例如在字节序问题上。
  • @Jubatian 我将变量中的值传递给模块,该模块将其发送到数据库。我必须保持这些值大约 1-60 秒,然后对其执行此操作(一次或多次)。

标签: c pointers void


【解决方案1】:

使用宏创建样板代码相对容易...

#define CASE(DATA_TYPE, TYPE) \
  case (DATA_TYPE): \
    switch (operation) { \
    case (OP_ONE_SHOT): \
      *(TYPE*)data_p = *(TYPE*)var_p; \
      break; \
    case (OP_COUNTER): \
      *(TYPE*)data_p += 1; \
      break; \
    case (OP_CURR_TIME): \
      *(TYPE*)data_p = (int)sytime; \
    ...etc...

static void GenData(USHORT data_type,
                     USHORT operation,
                     void *var_p,
                     void *data_p)
{
  switch (data_type)
  {
    CASE(DATA_INT, int)
    CASE(DATA_BYTE, BYTE)
    CASE(DATA_SHORT, short)
    ...etc...
  }
}

【讨论】:

  • 由于在宏中使用了data_pvar_p,我似乎无法编译它。 error C2100: illegal indirection
  • @user432209 如果没有看到您的确切代码,我无法猜测出什么问题。 Here's a self-contained example program 编译干净....
  • @Tony D 这帮我修好了。语法正确,现在开始调试。
  • @AlterMann:对此感到抱歉 - 我在发布之前只看到了您帖子的第一个版本,它似乎有很大不同,尽管我们都希望使用宏来简化代码。我看到您后来的编辑确实使我们的答案相似(并且您费心将反斜杠排成一行!-我太懒了)。干杯。
  • @TonyD,是的,很难排列反斜杠 :)
【解决方案2】:

另一种解决方案是将两个参数转换为支持所有必需操作并容纳您支持的最长类型(例如 long int)的单一类型。然后您可以执行操作并将结果转换回您需要的类型:

static long DoOperation(USHORT operation, long var, long data)
{
    switch(operation) {
        case(OP_ONE_SHOT):
            return data;
        case (OP_COUNTER):
            return data+1;
        [...etc...]
    }
}
static void GenData(USHORT data_type,
                     USHORT operation,
                     void *var_p,
                     void *data_p)
{
    switch(data_type) {
        case (DATA_INT):
            *(int*)data_p = (int)DoOperation(operation, *(int*)var_p, *(int*)data_p);
            break;
        case (DATA_BYTE):
            *(BYTE*)data_p = (BYTE)DoOperation(operation, *(BYTE*)var_p, *(BYTE*)data_p);
            break;
        [...etc...]
    }

}

当然,不能保证这种类型存在。

【讨论】:

  • 获得我对有价值建议的支持,尽管有些极端情况会变得更复杂,例如doubleint64_t 中的每一个都可以表示另一个不能表示的值。
  • 嗯,我的想法是避免多次实例化相同的代码。由于 float/double 的算术与整数算术完全不同,因此无法通过这种方法处理。如果将DoOperation 修改为使用int64_t 类型,则可以正确处理int64_t
【解决方案3】:

如果问题是算术运算无效,您可以使用一些正确类型的局部变量,将指针强制转换为局部变量的值,然后对局部变量执行运算,最后复制新的值返回指针...

/***********************************************************/
static void GenData(USHORT data_type,
                     USHORT operation,
                     void *var_p,
                     void *data_p)
/***********************************************************/
{
  int int_data, int_var;      

  switch (data_type)
  {
  case (DATA_INT):
    switch (operation) {
    case (OP_ONE_SHOT):
      *(int*)data_p = *(int*)var_p;
      break;
    case (OP_COUNTER):
      //*(int*)data_p += 1;
      int_data = *(int*)data_p;
      int_data +=1;
      *(int*)data_p= int_data;
      break;
    case (OP_CURR_TIME):
      *(int*)data_p = (int)sytime;
    case (OP_ELAPSED_TIME):
      //*(int*)data_p += delta_tick_time;
      int_data = *(int*)data_p;
      int_data += delta_tick_time;
      *(int*)data_p= int_data;
      break;
      //...and so on...

【讨论】:

  • 在您的代码中,*(int*)data_p= int_data; 和类似的行会覆盖调用者指定的内存地址处 int 的数据价值,即使该地址处的数据可能具有不同的大小(例如只有BYTE)。这会导致未定义的行为。
  • 我只展示了int类型的情况,如果类型是charlong,你必须使用正确类型的局部变量。调用函数时,您必须将 data_type 设置为传递类型的正确值。确保数据类型正确的唯一方法是为数据类型编写一个函数,并且不要使用 void 指针。
猜你喜欢
  • 1970-01-01
  • 2015-02-08
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
  • 2020-06-02
  • 1970-01-01
  • 2012-08-31
  • 1970-01-01
相关资源
最近更新 更多