有没有办法在std :: array之间切换

时间:2019-11-21 08:27:50

标签: c++ arrays refactoring

我有一些用C语言编写的旧代码,我想在C ++ 14中进行重构。我必须遇到一个我无法解决的问题。

在普通的C语言中,有一种方法可以在数组之间进行切换。

#define NELEMS(x)  (sizeof(x) / sizeof((x)[0]))

const int sz = (machine == 1) ? NELEMS (errors1) : NELEMS (errors2);
const struct Error *error = (machine == 1) ? &errors1[0] : &errors2[0];

上面的代码有效,但是不可读。我想使用std::array,因为它可以用越来越少的可读性代码轻松地进行操作。 std::vector使用堆内存,我希望结果可存储。

C ++代码三元运算符 const Error &error = (machine == 1) ? errors1 : errors2; 不能以这种方式使用,因为std::array<Error, 3> errors1std::array<Error, 5> errors2是不同的类型!那么如何使用std::array实现相同的功能?

旧版C代码

enum ErrorCode {

  ERR_NOT_FOUND,
  ERR_FORBIDDEN,
  ERR_INTERNAL,
  ERR_UNAVAILABLE,
  ERR_NO_RESPONSE,
  ERR_UNSUPPORTED,
  ERR_OUT_OF_MEMORY,
  ERR_TIMEOUT
};

struct Error {
  int id;
  int code;
  const char *str;
};



static const struct Error errors1[] = {
    {1001, ERR_NOT_FOUND, "Not Found"},
    {1002, ERR_FORBIDDEN, "Forbidden"},
    {1003, ERR_INTERNAL, "Internal Error"}
};



static const struct Error errors2[] = {
  {1004, ERR_UNAVAILABLE, "Temporarly Unavailable"},
  {1005, ERR_NO_RESPONSE, "No Response"},
  {1006, ERR_UNSUPPORTED, "Unsupported"},
  {1007, ERR_OUT_OF_MEMORY, "Insufficient Memory"},
  {1008, ERR_TIMEOUT, "Timeout"}
};

#define NELEMS(x)  (sizeof(x) / sizeof((x)[0]))

char *error_to_string (char machine, int id) {
    const int sz = (machine == 1) ? NELEMS (errors1) : NELEMS (errors2);
    const struct Error *error = (machine == 1) ? &errors1[0] : &errors2[0];
    const char *str = "Unknown";

    for (int i = 0; i < sz; i++) {
        if (error[i].id == id) {
           str = error[i].str;
           break;
        }
    }
    return str;
}

试图使用与C代码相同的功能来实现对C ++代码的现代化

std::array<Error, 3> errors1= {{
    {1001, ERR_NOT_FOUND, "Not Found"},
    {1002, ERR_FORBIDDEN, "Forbidden"},
    {1003, ERR_INTERNAL, "Internal Error"}
}};

std::array<Error, 5> errors2= {{
    {1004, ERR_UNAVAILABLE, "Temporarly Unavailable"},
    {1005, ERR_NO_RESPONSE, "No Response"},
    {1006, ERR_UNSUPPORTED, "Unsupported"},
    {1007, ERR_OUT_OF_MEMORY, "Insufficient Memory"},
    {1008, ERR_TIMEOUT, "Timeout"}
}};

const char *error_to_string (char machine, int id) {
    const char *str = (char*) "Unknown";
    const auto errors = (machine == 1) ? &errors1[0] : &errors2[0];

     for (auto error : errors) {
        if (error.id == id) {
            str = error.str;
            break;
        }
     }
    return str;
}

3 个答案:

答案 0 :(得分:3)

您可以使用lambda在给定数组中搜索错误。

const char *error_to_string(const char machine, const int id) {
    const auto find_error = [id](const auto &errors) {
        for (const Error &error : errors) {
            if (error.id == id) return error.str;
        }
        return "Unknown";
    };
    return machine == 1 ? find_error(errors1) : find_error(errors2);
}

要将其扩展到许多数组,只需使用switch

const char *error_to_string(const char machine, const int id) {
    const auto find_error = [id](const auto &errors) {
        for (const Error &error : errors) {
            if (error.id == id) return error.str;
        }
        return "Unknown";
    };
    switch (machine) {
        case 1: return find_error(errors1);
        case 2: return find_error(errors2);
        case 3: return find_error(errors3);
        // ...
    }
    // probably want to handle this properly
    assert(false);
    return "Invalid machine";
 }

答案 1 :(得分:2)

如果您有N个数组,而不仅仅是两个数组,那么简单的元编程可能会有所帮助:

template<char machine_id>
auto& get_errors() {
    if constexpr (machine_id == 0) 
        return errors1;
    else if constexpr (machine_id == 1) 
        return errors2;
    ...
    else
        return errorsN;
}

template<char machine_id>
const char* error_to_string_impl(char machine, int id) {
    if (machine == machine_id) {
        const auto& errors = get_errors<machine_id>();
        const auto fn = [id](Error e) { return e.id == id; };

        const auto it = std::find_if(errors.begin(), errors.end(), fn);
        if (it != errors.end())
            return it->str;
        return "Unknown";
    }

    if constexpr (machine_id + 1 < N)
        return error_to_string_impl<machine_id + 1>(machine, id);

    assert(false); // unreachable
    // return "Unknown machine";
}

const char* error_to_string(char machine, int id) {
    return error_to_string_impl<0>(machine, id);
}

get_error也可以通过重载来实现:

auto& get_errors(std::integral_constant<char, 0>) {
    return errors1;
}

auto& get_errors(std::integral_constant<char, 1>) {
    return errors2;
}

auto& get_errors(...) {
    return errorsN;
}

const auto& errors = get_errors(std::integral_constant<char, machine_id>{});

答案 2 :(得分:2)

在C ++ 20中,有std::span

const char* error_to_string (char machine, int id) {
    const auto errors = (machine == 1) ?
                            std::span<Error>{errors1} :
                            std::span<Error>{errors2};

     for (const auto& error : errors) {
        if (error.id == id) {
            return error.str;
        }
     }
    return "Unknown";
}

以前,您可以使用模板:

template <typename Container>
const char* error_to_string(const Container& errors) {
     for (const auto& error : errors) {
        if (error.id == id) {
            return error.str;
        }
     }
    return "Unknown";
}

const char* error_to_string (char machine, int id) {
    return (machine == 1) ? error_to_string(errors1) : error_to_string(errors2);
}