基于C++中的枚举值投掷void*。

问题描述 投票:0回答:2

我正在使用python C Api用C++编写一个Python库。在那里我有大约25个函数,它们都接受两个字符串。由于Python可能会将字符串保存在utf81632中(当char需要更大尺寸的时候,整个字符串将使用更大的尺寸)。当检查字符串的种类时,你会得到一个介于0和4之间的枚举值。04应该处理为utf32,1为utf8,2为utf16。所以我目前为每个组合都设置了一个嵌套开关。

下面的例子显示了我的代码中如何处理这些元素。random_func 我的每个函数都是不同的,是一个模板,可以接受任何类型的string_view。这种写代码的方式导致每个接受两个字符串的函数都有大约100行的模板。

有没有一种方法可以处理所有这些情况,而不需要这种巨大的代码重复,并且不牺牲性能?

double result = 0;
Py_ssize_t len_s1 = PyUnicode_GET_LENGTH(py_s1);
void* s1 = PyUnicode_DATA(py_s1);

Py_ssize_t len_s2 = PyUnicode_GET_LENGTH(py_s2);
void* s2 = PyUnicode_DATA(py_s2);

int s1_kind = PyUnicode_KIND(py_s1);
int s2_kind = PyUnicode_KIND(py_s2);

switch (s1_kind) {
case PyUnicode_1BYTE_KIND:
    switch (s2_kind) {
    case PyUnicode_1BYTE_KIND:
        result = random_func(
            basic_string_view<char>(static_cast<char*>(s1), len_s1),
            basic_string_view<char>(static_cast<char*>(s2), len_s2));
        break;
    case PyUnicode_2BYTE_KIND:
        result = random_func(
            basic_string_view<char>(static_cast<char*>(s1), len_s1),
            basic_string_view<char16_t>(static_cast<char16_t*>(s2), len_s2));
        break;
    default:
        result = random_func(
            basic_string_view<char>(static_cast<char*>(s1), len_s1),
            basic_string_view<char32_t>(static_cast<char32_t*>(s2), len_s2));
        break;
    }
    break;
case PyUnicode_2BYTE_KIND:
    switch (s2_kind) {
    case PyUnicode_1BYTE_KIND:
        result = random_func(
            basic_string_view<char16_t>(static_cast<char16_t*>(s1), len_s1),
            basic_string_view<char>(static_cast<char*>(s2), len_s2));
        break;
    case PyUnicode_2BYTE_KIND:
        result = random_func(
            basic_string_view<char16_t>(static_cast<char16_t*>(s1), len_s1),
            basic_string_view<char16_t>(static_cast<char16_t*>(s2), len_s2));
        break;
    default:
        result = random_func(
            basic_string_view<char16_t>(static_cast<char16_t*>(s1), len_s1),
            basic_string_view<char32_t>(static_cast<char32_t*>(s2), len_s2));
        break;
    }
    break;
default:
    switch (s2_kind) {
    case PyUnicode_1BYTE_KIND:
        result = random_func(
            basic_string_view<char32_t>(static_cast<char32_t*>(s1), len_s1),
            basic_string_view<char>(static_cast<char*>(s2), len_s2));
        break;
    case PyUnicode_2BYTE_KIND:
        result = random_func(
            basic_string_view<char32_t>(static_cast<char32_t*>(s1), len_s1),
            basic_string_view<char16_t>(static_cast<char16_t*>(s2), len_s2));
        break;
    default:
        result = random_func(
            basic_string_view<char32_t>(static_cast<char32_t*>(s1), len_s1),
            basic_string_view<char32_t>(static_cast<char32_t*>(s2), len_s2));
        break;
    }
    break;
}
c++ python-c-api
2个回答
2
投票

在函数中使用变体把复杂度去掉。

using python_string_view = std::variant<std::basic_string_view<char>,
    std::basic_string_view<char16_t>,
    std::basic_string_view<char32_t>;

python_string_view decode_python_string(python_string py_str)
{
    Py_ssize_t len_s = PyUnicode_GET_LENGTH(py_str);
    void* s = PyUnicode_DATA(py_str);
    int s_kind = PyUnicode_KIND(py_str);

    switch (s_kind) {
        //return correct string_view here
    }
}

int main()
{
    python_string s1 = ..., s2 = ...;
    auto v1 = decode_python_string(s1);
    auto v2 = decode_python_string(s2);
    std::visit([](auto&& val1, auto&& val2) {
        random_func(val1, val2);
    }, v1, v2);
}

不过性能方面我不确定。


-1
投票

就它的价值而言。

不同的字符类型所带来的不同之处就在于 你在提取字符值的那一刻 random_func (如果我没猜错的话,需要九个模板的特殊化)。

通过使用最大的类型获取所有情况下的字符,并在必要时屏蔽或移出额外的字节,你将接近一个解决方案。与其说是模板化,不如说是传递一个合适的掩码和一个跨步信息。比如说

for (char32_t* c= (char32_t*)s1; c &= mask, c != 0; c= (char32_t*)((char*)c + stride))
{
    …
}

不幸的是,不算额外的掩码操作,你会遇到一堵墙,因为你可能需要在字符串的一端获取太多的字节,导致非法的内存访问。

© www.soinside.com 2019 - 2024. All rights reserved.