为什么我们需要向下转换一个变量,即使函数会在返回之前再次向上转换它?
Why do we need to downcast a variable even if the function will upcast it again right before returning?
在阅读一本关于现代 C++ 的书时,我看到了一段让我感到困惑的代码片段。编写的代码是为 8 位 AVR 微控制器设置 PWM(16 位定时器)。代码是这样的:
class pwm_base : private util::noncopyable
{
public:
typedef std::uint_fast16_t duty_type;
pwm_base(const duty_type resol,
const duty_type duty = 0U) : resolution(resol),
counter (0U),
duty_cycle(duty),
shadow (duty) { }
duty_type get_resolution() const { return resolution; }
void set_duty(const duty_type duty)
{
// Set a new duty cycle in the shadow register.
mcal::irq::disable_all();
shadow = static_cast<std::uint_fast8_t>((std::min)(duty, get_resolution())); // ???? (1)
mcal::irq::enable_all();
}
duty_type get_duty() const
{
// Retrieve the duty cycle.
mcal::irq::disable_all();
const volatile std::uint_fast8_t the_duty = duty_cycle; // ???? (2)
mcal::irq::enable_all();
return the_duty;
}
virtual void service() = 0;
protected:
const duty_type resolution;
duty_type counter;
duty_type duty_cycle;
duty_type shadow;
};
我对 ????
指示的行有问题。可以清楚地看到,shadow
和 duty_cycle
都被定义为 uint_fast16_t
。因此,这意味着它们必须至少为 16 位。如果是这样,为什么作者在第 ???? (1)
行将 min 方法的结果向下转换为 uint_fast8_t
而不是转换为 uint_fast16_t
?而且,为什么在 ???? (2)
行他再次将变量向下转换为 uint_fast8_t
,即使函数 return 类型是 uint_fast16_t
?这些向下转换是必需的吗?他们的目的是什么?
此代码似乎严重过度设计,只要允许使用 C++,这始终是最大的危险。该语言倾向于鼓励让事情不必要地复杂化,而不是利用 KISS principle.
硬性要求是:在给定的硬件上,CPU 在 8 位类型下工作速度最快,但 PWM 寄存器是 16 位。期间.
因此,任何描述周期或占空比的变量都需要正好 16 位。声明为uint_fast16_t
是没有意义的,因为相应的硬件寄存器总是恰好是16位,不多也不少。
通常,uint_fast
类型仅在计划以某种方式将其移植到 32 位系统时才有用,但不太可能存在具有相同 PWM 外设的 32 位 MCU。因此在这种情况下 uint_fast
只是无用的噪音,因为代码永远不会被移植。
据推测,转换为 uint8_t
会将 16 位值截断为最低 8 位。如果是这样,则转换不正确,它不应该是 static_cast<std::uint_fast8_t>
,而是 static_cast<std::uint8_t>
。否则我不太了解代码应该做什么,我假设在某处更新占空比。
此外,禁用中断屏蔽作为重入保护功能可能没有意义,因为这会导致意外的时序问题。如果要从 ISR 内部使用此 class,则另一种保护机制可能更合适,例如信号量或原子 access/inline asm。 PWM 尤其挑剔 when/how 你改变占空比,否则你可能会出现故障。
总的来说,我强烈建议不要在古老的、资源严重受限的 8 位微控制器上使用 C++。或者在学习嵌入式系统时使用古老的 8 位微控制器而不是 ARM,8 位比 32 位更难用 C(或 C++)编程。
在阅读一本关于现代 C++ 的书时,我看到了一段让我感到困惑的代码片段。编写的代码是为 8 位 AVR 微控制器设置 PWM(16 位定时器)。代码是这样的:
class pwm_base : private util::noncopyable
{
public:
typedef std::uint_fast16_t duty_type;
pwm_base(const duty_type resol,
const duty_type duty = 0U) : resolution(resol),
counter (0U),
duty_cycle(duty),
shadow (duty) { }
duty_type get_resolution() const { return resolution; }
void set_duty(const duty_type duty)
{
// Set a new duty cycle in the shadow register.
mcal::irq::disable_all();
shadow = static_cast<std::uint_fast8_t>((std::min)(duty, get_resolution())); // ???? (1)
mcal::irq::enable_all();
}
duty_type get_duty() const
{
// Retrieve the duty cycle.
mcal::irq::disable_all();
const volatile std::uint_fast8_t the_duty = duty_cycle; // ???? (2)
mcal::irq::enable_all();
return the_duty;
}
virtual void service() = 0;
protected:
const duty_type resolution;
duty_type counter;
duty_type duty_cycle;
duty_type shadow;
};
我对 ????
指示的行有问题。可以清楚地看到,shadow
和 duty_cycle
都被定义为 uint_fast16_t
。因此,这意味着它们必须至少为 16 位。如果是这样,为什么作者在第 ???? (1)
行将 min 方法的结果向下转换为 uint_fast8_t
而不是转换为 uint_fast16_t
?而且,为什么在 ???? (2)
行他再次将变量向下转换为 uint_fast8_t
,即使函数 return 类型是 uint_fast16_t
?这些向下转换是必需的吗?他们的目的是什么?
此代码似乎严重过度设计,只要允许使用 C++,这始终是最大的危险。该语言倾向于鼓励让事情不必要地复杂化,而不是利用 KISS principle.
硬性要求是:在给定的硬件上,CPU 在 8 位类型下工作速度最快,但 PWM 寄存器是 16 位。期间.
因此,任何描述周期或占空比的变量都需要正好 16 位。声明为uint_fast16_t
是没有意义的,因为相应的硬件寄存器总是恰好是16位,不多也不少。
通常,uint_fast
类型仅在计划以某种方式将其移植到 32 位系统时才有用,但不太可能存在具有相同 PWM 外设的 32 位 MCU。因此在这种情况下 uint_fast
只是无用的噪音,因为代码永远不会被移植。
据推测,转换为 uint8_t
会将 16 位值截断为最低 8 位。如果是这样,则转换不正确,它不应该是 static_cast<std::uint_fast8_t>
,而是 static_cast<std::uint8_t>
。否则我不太了解代码应该做什么,我假设在某处更新占空比。
此外,禁用中断屏蔽作为重入保护功能可能没有意义,因为这会导致意外的时序问题。如果要从 ISR 内部使用此 class,则另一种保护机制可能更合适,例如信号量或原子 access/inline asm。 PWM 尤其挑剔 when/how 你改变占空比,否则你可能会出现故障。
总的来说,我强烈建议不要在古老的、资源严重受限的 8 位微控制器上使用 C++。或者在学习嵌入式系统时使用古老的 8 位微控制器而不是 ARM,8 位比 32 位更难用 C(或 C++)编程。