当前位置: 首页 > 知识库问答 >
问题:

有没有办法生成参数化的用户定义文字?

齐元章
2023-03-14

不久前,我有了一个关于“参数化”用户定义文本的想法,并且想知道在当前的C标准中是否有任何方法可以做到这一点。

基本上,这个想法是有一个用户定义的文本,其行为可以根据一些参数进行调整。作为一个简单的例子,我选择了一个“定点”字面值,它将浮点数转换为整数;该参数是小数位数的精度。

这只是一个练习,因为我不确定这在实际应用中是否有用。

我的第一个想法是这样的:

namespace fp_impl {
    constexpr int floor(long double n) {
        return n;
    }

    constexpr int pow10(int exp) {
        return exp == 0 ? 1 : 10 * pow10(exp - 1);
    }

    template<int i>
    constexpr int fixed_point(long double n) {
        return floor(n * pow10(i));
    }

    namespace fp2 {
        constexpr int operator"" _fp (long double n) {
            return fixed_point<2>(n);
        }
    }

    namespace fp4 {
        constexpr int operator"" _fp (long double n) {
            return fixed_point<4>(n);
        }
    }
}

template<int prec> struct fp;
template<> struct fp<2> {
    namespace lit = fp2;
};
template<> struct fp<4> {
    namespace lit = fp4;
};

int main() {
    {
        using namespace fp<2>::lit;
        std::cout << 5.421_fp << std::endl; // should output 542
    }
    {
        using namespace fp<4>::lit;
        std::cout << 5.421_fp << std::endl; // should output 54210
    }
}

但是,它不会编译,因为类范围中不允许使用名称空间别名。(它还存在一个问题,即要求您不能手动定义每个版本的运算符“”\u fp)所以我决定尝试一下宏:

namespace fp {
    namespace detail {
        constexpr int floor(long double n) {
            return n;
        }

        constexpr int pow10(int exp) {
            return exp == 0 ? 1 : 10 * pow10(exp - 1);
        }

        template<int i>
        constexpr int fixed_point(long double n) {
            return floor(n * pow10(i));
        }
    }
}

#define SPEC(i) \
    namespace fp { \
        namespace precision##i { \
            constexpr int operator"" _fp(long double n) { \
                return fp::detail::fixed_point<i>(n); \
            } \
        } \
    }
SPEC(2); SPEC(4);
#undef SPEC
#define fp_precision(i) namespace fp::precision##i

int main() {
    {
        using fp_precision(2);
        std::cout << 5.421_fp << std::endl;
    }
    {
        using fp_precision(4);
        std::cout << 5.421_fp << std::endl;
    }
}

这是可行的,尽管它仍然需要为您想要使用的每个精度使用SPEC()宏。当然,可以使用一些预处理器技巧来实现从0到100的每个值,但是我想知道是否有更像模板解决方案的东西,其中每个都根据需要实例化。我有一个模糊的想法,在模板类中使用运算符“”声明为朋友函数,尽管我怀疑这也行不通。

作为说明,我确实尝试了模板


共有3个答案

劳星晖
2023-03-14

解决这个问题不需要宏。由于问题涉及处理文字数字(例如整数或浮点格式的数字),因此可以使用文字运算符的模板定义和模板元编程在编译时完全完成这项工作。

要进行定点文字转换,可以使用带有无符号long-long的整数文字运算符,例如。,

some_type operator "" _fp(unsigned long long num)
{
  // code
}

(或者使用长双精度,可能会损失精度)但这会导致一切都在运行时发生。

第3和第4段中的第2.14.8节(用户定义的Lierals[lex.ext])中的C11定义了文字运算符变体,包括整数和浮点文本的模板版本!不幸的是,第5和第6段没有为字符串和字符文本定义模板版本。这意味着这种技术只能用于整数和浮点文本。

因此,根据C 11第2.14.8节,上述_fpliteral运算符可以写成:

template <char... Digits>
constexpr some_type operator "" _fp()
{
  return process_fp<2, Digits...>::to_some_type();
}

例如,其中2是来自OP的int i模板参数的值,而some_type是返回类型所需的任何类型。请注意,模板参数是char,而不是int或其他数字。还要注意,文字运算符没有参数。因此,需要像Digit-'0'这样的代码来将该字符的数值转换为整数。此外,Digits...将以从左到右的顺序处理。

现在,可以将模板元编程与process_fp结合使用,其正向声明如下所示:

template <int i, char... Digits>
struct process_fp;

并且会有一个名为静态constexpr方法来计算并返回所需的编译时结果。

人们可能还希望有一个有意义、简单的例子来说明这一点。去年,我写了一段代码(下面的链接),当这样使用时:

int main()
{
  using namespace std;

  const unsigned long long bits =
    11011110101011011011111011101111_binary;
  cout << "The number is: " << hex << bits << endl;
}

将在编译时将二进制数11011110101011011011111101111转换为无符号long,并将其存储为位。关于使用上述模板元编程技术的完整代码和解释,请参阅我的博客文章《使用C文字运算符》。

钱季
2023-03-14

用户定义的文字函数将文字本身作为其唯一参数。您可以在函数之外使用状态,例如使用全局或线程局部变量,但这不是很干净。

如果参数始终是编译时常量,并且它是数字的一部分,则将其传递给文本本身。这需要编写一个操作符“”(char const*,std::size\t)重载或模板

不过,您必须在现有的浮点语法中使用这样的参数。尽管C定义了一个非常开放的预处理数字构造,但用户定义的文本必须由一个附加了ud后缀标识符的有效令牌构成。

您可能会考虑使用字符串而不是数字,但是模板选项会消失。

邹涵畅
2023-03-14

您可以返回一个类类型,该类类型的操作符()(int)重载自您的文字操作符

5.421_fp(2);
 类似资料:
  • 问题内容: 有没有办法在Linux中创建用户定义的信号?我的信号(信号号)应该与任何现有信号号都不匹配。 换句话说,我想创建自己的唯一信号,该信号将由我的处理程序注册并捕获。 可能吗?如果是,怎么办? 提前致谢。 问题答案: 您无法添加或注册自己的SIGWHATEVER。 请参阅,它是固定大小。见熊陷阱。

  • 我有一个枚举类来存储一些像这样的分类值。 每种类型都有pydantic模型。例如: 我有这样的字典: 我有一个FastAPI路由,用户需要输入字段类型名称作为字符串(从FastAPI文档中的下拉列表),并根据选择的类型提供类型属性。如果用户选择type=“images”,则会提供相应的pydantic模型“ImageModel”供用户填写,以此类推。 选择类型名称后,有没有办法产生对应的pydan

  • 我想知道是否有一种方法可以基于字符串生成相同的UUID 我尝试使用UUID,它似乎没有提供此功能。

  • 问题内容: 假设我有一个PHP函数foo: 有什么办法只能指定第二个可选参数? 例: 问题答案: PHP本身不支持命名参数作为函数。但是,有一些方法可以解决此问题: 使用数组作为该函数的唯一参数。然后,您可以从数组中提取值。这允许在数组中使用命名参数。 如果要根据上下文允许可选数量的参数,则可以使用func_num_args和func_get_args而不是在函数定义中指定有效参数。然后,根据参数

  • 我正在为我的项目使用带有azure函数的typescript和node。我想为我的项目自动生成openAPI定义文件,而不是手动创建一个。我看到了https://www.nuget.org/packages/Aliencube.AzureFunctions.Extensions.OpenApi/用C#处理这个问题的包,但我目前找不到与之相当的typescript。是否有与typescript等效的

  • 问题内容: 我有一个使用处理程序发布可运行实例的线程。它工作得很好,但是我很好奇我如何将参数传递给Runnable实例使用?也许我只是不了解此功能的工作原理。 为了避免出现“您为什么需要此问题”的问题,我有一个线程化的动画,必须调出UI线程来告诉它实际绘制的内容。 问题答案: 只需使用接受参数的构造函数实现的类就可以了, 您可以使用参数化构造函数创建Runnable类的实例。