在C中映射非顺序值的最佳方法

时间:2019-07-12 18:32:18

标签: c algorithm data-structures error-handling

我有一个非常人为的错误代码系统(数百种非顺序值,C语言),并且将人为地转换为人类可读的错误消息。

我正在考虑重构它,但是我不确定将值映射到其对应字符串的最佳方法。有小费吗?

错误代码是唯一的,不能用于编写新代码。

构建错误代码是为了使每个代码都有一个 base 值,以指定其所属的模块。警告和状态消息有一些偏移量:

#define SOME_MODULE_ERR_BASE    0x120000
#define OTHER_MODULE_ERR_BASE   0x130000

#define STATUS_OFFSET  1000
#define WARNING_OFFSET 2000

/* Error codes as defined as needed, sequentially from the base value */
#define SOME_MODULE_ERR_NOT_FOUND   (SOME_MODULE_ERR_BASE + 1)
#define SOME_MODULE_ERR_BAD_CRC     (SOME_MODULE_ERR_BASE + 2)
#define SOME_MODULE_STATUS_BUSY     (SOME_MODULE_ERR_BASE + STATUS_OFFSET + 1)
#define SOME_MODULE_WARNING_INCOMPLETE (SOME_MODULE_ERR_BASE + WARNING_OFFSET + 1)

#define OTHER_MODULE_ERR_BAD_CRC     (OTHER_MODULE_ERR_BASE + 1)
#define OTHER_MODULE_ERR_NOT_FOUND   (OTHER_MODULE_ERR_BASE + 2)
/*...*/

我们目前有代码来打印适当的错误字符串,但是转换过程(从错误代码到字符串)非常奇怪,以至于即使添加新的错误代码也很麻烦。我想改进我们的日志记录系统,但是这个愚蠢的代码正在妨碍您的工作。通过重构错误消息打印,我将能够重构日志记录系统。

基本上,我想要这个:

result = SomeModuleFunc();
printf("SomeModuleFunc returned %s\n", ErrToString(result));

它将适当地打印SOME_MODULE_ERR_NOT_FOUNDSOME_MODULE_ERR_BAD_CRCSOME_MODULE_STATUS_BUSY等。

在我看来,最简单的方法只是建立指向适当字符串的巨型switch-case语句,但这也许只是因为我想不到一个好的数据结构来简化映射过程。

3 个答案:

答案 0 :(得分:1)

除非错误代码很大,或者如果您的资源非常有限,我的解决方案是仅使用大量的char指针指向消息。

const char *error_msg[] = {
    "", "", "Out of memory", "", "Out of disk space", 
    "", "", "", "Unauthorized user" ... 
};

这很简单并且可以正常工作。如果最高的代码很高(您的情况似乎如此),则可能会遇到问题。在这种情况下,请使用指向指针的指针。

const char **error_msg;

void init_error() 
{
    error_msg = calloc(size, sizeof(*error_msg));
    error_msg[2] = "Out of memory";
    error_msg[4] = "Out of disk space";
    error_msg[8] = "Unauthorized user";
}

使用后一种方法,您无法在全局空间中进行初始化,因此请使用init函数并在main的开头进行调用。但是只要error_msg是全局的,您就可以使用此功能。

const char *ErrToString(size_t code) 
{ 
    return error_msg[code]; 
}

当然,这种方法浪费了内存,但是除非您的资源非常有限,否则这不是问题。基数0x130000大约为120万(十进制)。因此,如果指针为8个字节,则该大小将小于10MB,这在现代计算机上已经没有了。它绝对比任何哈希或二进制搜索快得多。通常,当您必须生成错误消息时,生成错误消息的性能并不是最大的问题,但是如果您关心它可能是值得了解的。

优点:

  • 实施起来非常简单
  • 快速点亮

缺点:

  • 浪费记忆力

简单通常是最好的。不要使事情复杂化。

答案 1 :(得分:0)

您可以使用某种哈希表。 如果您的errcode-set是固定的,则可以选择一些“魔术除法器”,并将错误代码除以它,然后将提醒用作数组中的索引,例如:

const char *p_msg = err_msg_arr[err_code % MAGIC];

当然,您的数组的大小必须为[MAGIC];

要找出这个神奇的价值,只需编写一个简单的程序,其中包含代码列表,然后尝试增加MAGIC的候选对象,直到找到一组唯一的遗迹。

此类程序和输入数据文件的示例:

#!/usr/local/bin/perl -w

my %in_set;

while(<>) {
  chomp;
  my ($errcode, $str) = m/(\d+)\s+(.+)/;
  $in_set{$errcode} = $str;
  #  print STDERR "added: [$errcode] => [$str]\n";
}

my $size = scalar keys %in_set;

my @rems;
for(my $magic = $size; $magic < $size * 10; $magic++) {
  @rems = (-1)x$magic;
  foreach (keys %in_set) {
    my $rem = $_ % $magic;
    goto NXT if $rems[$rem] >= 0;
    $rems[$rem] = $_;
  }
  # found the unque set of remains
  print "const char *err_msg[$magic] = {\n";
  for(my $m = 0; $m < $magic; $m++) {
    print $rems[$m] < 0? "\tNULL,\n" : "\t\"$in_set{$rems[$m]}\",\n";
  }
  exit;
  NXT:
} # for magic

数据文件:

$ cat err_samples.txt 
100 error num 1
250 err 2
5000 err #1

运行结果:

 ./errgen.pl err_samples.txt 
const char *err_msg[8] = {
    "err #1",
    NULL,
    "err 2",
    NULL,
    "error num 1",
    NULL,
    NULL,
    NULL,

答案 2 :(得分:0)

处理此问题的最简单方法是创建一个包含错误代码和错误消息的结构,然后创建一个包含每个代码/消息的数组,其中错误代码的数字顺序递增:

struct error_codes {
    int code;
    const char *message;
};

struct error_codes codes[] = {
    { 4, "file not found" },
    { 10, "out of memory" },
    { 12, "can't connect" },
    ...
};

然后创建一个函数,对给定的错误代码对列表进行二进制搜索:

const char *error_message(int code)
{
    int len = sizeof(codes) / sizeof(codes[0]);
    int start = 0, end = len - 1;

    while (start <= end) {
        int idx = start + ((end - start) / 2);

        if (codes[idx].code < code) {
            end = idx - 1;
        } else if (codes[idx].code > code) {
            start = idx + 1;
        } else {
            return codes[idx].message;
        }
    }
    return "unknown error code";            
}

具有几百个错误代码,这不会超过10次迭代。

如果消息总数有点接近最大值,并且所有值均为非负数,则可以仅创建一个字符串数组,其索引为错误代码,并为未使用的错误代码放入一个虚拟值。但是,鉴于高错误代码库,那将意味着数组很大,这是不可行的。