c++在变化的原语列表上执行操作

kyxcudwk  于 2022-12-15  发布在  其他
关注(0)|答案(2)|浏览(122)

我正在为一种简单的脚本语言编写一个解释器,在对变量执行算术运算时遇到了一些问题,我需要创建两个原语列表,其中每个元素都可以是不同的原语类型,即uint、int、float和bool,沿着不同位数的numeric:第8、16、32和64段。
然后,我需要在列表1中的元素和列表2中的元素之间执行一些算术运算,并存储结果,但我找不到任何优雅的方法来完成它。
我所想到的最好的方法是创建一个元素可以容纳的所有基元类型的并集,并创建一个枚举来指定使用哪一个,然后创建一个switch语句来从并集中获取正确的基元类型,这样就可以从列表1中获取值,然后再编写一个switch语句来从列表2中获取值。第三个用于找出结果的变量类型。
在此示例中,GenericPrimitive是一个类,它包含基元的联合以及指定要使用哪个联合类型的枚举:

void DoOperation(vector<GenericPrimitive> first, vector<GenericPrimitive> second, GenericPrimitive& result)
{
    switch (first.at(0).PrimitiveType())
    {
        case PT_UINT8_T: return DoOperationOnT<uint8_t>(first.at(0).GetValueAsUInt8T(), second, result);
        ...

template <class T>
void DoOperationOnT(T firstValue, vector<GenericPrimitive> second, GenericPrimitive& result)
{
    switch (second.at(0).PrimitiveType())
    {
        case PT_INT16_T: return DoOperationOnTAndU<T, int16_t>(firstValue, second.at(0).GetValueAsInt16T(), result);
        ...

template <class T, class U>
void DoOperationOnTAndU(T firstValue, U secondValue, GenericPrimitive& result)
{
    switch (result.PrimitiveType())
    {
        case FLOAT: StoreOpResultAsFloat<T, U>(firstValue, secondValue, result);
        ...

template <class T, class U>
void StoreOpResultAsFloat(T firstValue, U secondValue, GenericPrimitive& result)
{
    switch (OperationType)
    {
        case ADDITION: result.SetFloat(AddValues<T, U, float>(firstValue, secondValue));
        ....

template <class T, class U, class V>
V AddValues(T first, U second)
{
    return (V)(T + U);
}

所有这些switch语句都是低效的。有没有什么更简单或更快的方法是我遗漏的?我只想知道AddValues函数的类型T、U和V,而不必每次都重新计算它们。(或者做一些其他的事情来达到同样的效果)

6rqinv9w

6rqinv9w1#

我会先做侧写再做假设,但还是:我假设你忽略了一个重要的常见案例:first.[0].PrimitiveType()==first.[0].PrimitiveType()-在这种情况下,您只需要一个switch。混合模式的运算往往很少。
(Also,我假设.at(0)是一个无意的边界检查-在有意使用.at的代码中确实应该有一个注解)

bvn4nwqk

bvn4nwqk2#

我将从重新评估模板的使用开始,如果目标是消除switch语句的重复使用,我建议使用函子、枚举和变量模板:

struct AddValuesFunctor
{
  template<class V, class T, class U>
  V operator()(T firstValue, U secondValue)
  {
    return static_cast<V>(firstValue) + static_cast<V>(secondValue)
  }
}

看起来您已经在使用类型枚举了,但是为了涵盖我所有的基础,我将提到您在下一段代码中将需要一个类型枚举。
下一步是添加一个可变参数模板,如下所示:

template <class FuncT, class... ArgsT>
auto RunExemplarSwitch(FuncT&& func, Typing enumValueRepresentingType, ArgsT&&... args)
{
  switch(enumValueRepresentingType)
  {
   case Typing::FLOAT: {
     return func.template operator()<float>(std::forward<ArgsT>(args)...);
   }
   case Typing::Boolean: {
     return func.template operator()<bool>(std::forward<ArgsT>(args)...);
   }
   ...
   default:
   { 
     throw std::runtime_error("Unhandled Type Passed In");
   }
}

由于您的模板结构应该能够确定firstValuesecondValue的值,可变参数模板应该实现class V的类型化
实际的函数调用如下所示[假设您正在解析整个向量,并且它们的长度相等]:

vector<GenericPrimitive> first;
vector<GenericPrimitive> second;
for(size_t sharedIndex = 0; sharedIndex < first.size(); sharedIndex++)
{
  RunExemplarSwitch(AddValuesFunctor{}, Typing::FLOAT, first[sharedIndex], second[sharedIndex]);
}

这可能需要进行调整以适应您的特定用例,但希望使用这样的实现将有助于减少交换机的使用。
这是我的第一篇文章,所以我道歉,如果它没有达到标准。

相关问题