【问题标题】:Is it possible to take an arbitrary pointer as input argument in C++?是否可以在 C++ 中将任意指针作为输入参数?
【发布时间】:2017-01-03 11:14:23
【问题描述】:

我想创建一个类来管理 Cortex-M3 设备的位带功能。对于那些不知道的人:处理器将特定区域内的每一位映射到一个完整的单词。这允许对特定位进行原子集操作。该类本身适用于 std::uintptr_t。对于构造函数,我想允许一个任意指针,因为我不在乎它指向什么。它可能是设备头中定义的一些结构。

我当前的实现为构造函数提供:

Bitband(uintptr_t address, uint32_t bitNumber);
Bitband(void * ptr, uint32_t bitNumber);

我的应用程序这样调用构造函数:

Bitband foo(reinterpret_cast<uintptr_t>(&gpioPort->IDR), pin);

如果我遗漏他 reinterpret_cast,我将无法获得“uintptr_t”和“void *”的已知转换。 有没有一种干净的方法可以摆脱每次调用的 reinterpret_cast 并将任意指针作为构造函数的参数?

编辑:这是我当前的 Bitband 类代码以及我用于打开或关闭 LED 的用途:

bitband.hpp

#pragma once

#include <stdint.h>

class Bitband
{
public:
  Bitband(uintptr_t address, uint32_t bitNumber);
  Bitband(void * address, uint32_t bitNumber);

  inline void Set(bool val) const
  {
    uint32_t * const pData = reinterpret_cast<uint32_t *>(this->bbAddress);
    *pData = val;
  }

  inline bool Get() const 
  {
    uint32_t * const pData = reinterpret_cast<uint32_t *>(this->bbAddress);
    return *pData;
  }
private:
  static uintptr_t GetBitBandAddress(uintptr_t address, uint32_t bit);
  static bool IsSramAddress(uintptr_t address);
  static bool IsPeripheralAddress(uintptr_t address);

  uintptr_t const bbAddress;

/* Constants for bit band calculation for SRAM */
  static uintptr_t const sramStartAddress = 0x20000000;
  static uintptr_t const sramEndAddress   = 0x200FFFFF;
  static uintptr_t const sramBbBaseAddress = 0x22000000;

/* Constants for bit band calculation for Peripherals */
  static uintptr_t const peripheralsStartAddress = 0x40000000;
  static uintptr_t const peripheralsEndAddress   = 0x400FFFFF;
  static uintptr_t const peripheralsBbBaseAddress = 0x42000000;
};

bitband.cpp

#include "bitband.hpp"
#include <cassert>

Bitband::Bitband(uintptr_t address, uint32_t bitNumber) :
  bbAddress(GetBitBandAddress(address, bitNumber)) {}

Bitband::Bitband(void * address, uint32_t bitNumber) :
  bbAddress(GetBitBandAddress(reinterpret_cast<uintptr_t>(address), bitNumber)) {}

uintptr_t Bitband::GetBitBandAddress(uintptr_t const address,
                                      uint32_t const bitNumber)
{
  uintptr_t bbBase;
  uintptr_t regionStartAddress;

  assert(Bitband::IsPeripheralAddress(address)
          || Bitband::IsSramAddress(address));

  /* Set the parameters depending on wether we are in peripherals region or sram
    region. */
  if(Bitband::IsSramAddress(address))
  {
    bbBase = Bitband::sramBbBaseAddress;
    regionStartAddress = Bitband::sramStartAddress;
  }
  else if(Bitband::IsPeripheralAddress(address))
  {
    bbBase = Bitband::peripheralsBbBaseAddress;
    regionStartAddress = Bitband::peripheralsStartAddress;
  }
  else
  {
    /* Invalid parameter */
    __breakpoint(0);
  }

  uintptr_t byteOffset = address - regionStartAddress;
  auto bitWordOffset = (byteOffset * 32) + (bitNumber * sizeof(uint32_t));
  auto bitWordAddr = bbBase + bitWordOffset;

  return bitWordAddr;
}

bool Bitband::IsSramAddress(uintptr_t address)
{
  return (address >= Bitband::sramStartAddress)
            && (address <= Bitband::sramEndAddress);
}

bool Bitband::IsPeripheralAddress(uintptr_t address)
{
  return (address >= Bitband::peripheralsStartAddress)
            && (address <= Bitband::peripheralsEndAddress);
}

它被我的班级领导使用(用于测试我只是打开/关闭一些 LED)

led.hpp

#pragma once

#include <stdint.h>
#include "stm32l1xx.h"                  // Keil::Device:Startup
#include "bitband.hpp"

class Led
{
public:
  Led(GPIO_TypeDef * const ledPort, uint16_t ledPin);
  inline void Set(bool newState) { this->ledOutputBitBand.Set(!newState); }
private:
  Bitband ledOutputBitBand;
};

led.cpp

#include <led.hpp>
#include <cassert>

Led::Led(GPIO_TypeDef * const port, uint16_t const pin) :
    ledOutputBitBand(reinterpret_cast<uintptr_t>(&port->ODR), pin)
{
  assert(pin < 16);

  /* Set port mode to push pull */
  port->MODER |= 1 << ( 2 * pin);
}

在主应用程序中使用

Led greenLed(GPIOD, 0);
greenLed.Set(true);

如果我遗漏了 reinterpret_cast,我会收到以下消息:

Src/led.cpp(5): error: no matching constructor for initialization of 'Bitband'
    ledOutputBitBand(&port->ODR, pin)
    ^                ~~~~~~~~~~~~~~~

./Inc/bitband.hpp(9): note: candidate constructor not viable: no known conversion from 'volatile uint16_t *' (aka 'volatile unsigned short *') to 'uintptr_t' (aka 'unsigned int') for 1st argument; remove &
  Bitband(uintptr_t address, uint32_t bitNumber);
  ^

./Inc/bitband.hpp(10): note: candidate constructor not viable: no known conversion from 'volatile uint16_t *' (aka 'volatile unsigned short *') to 'void *' for 1st argument
  Bitband(void * address, uint32_t bitNumber);
  ^

./Inc/bitband.hpp(6): note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 2 were provided
class Bitband
      ^

./Inc/bitband.hpp(6): note: candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 2 were provided
1 error generated.

所以我想如果我想在另一个上下文中使用类 Bitband,我必须再次使用 reinterpret_cast?

【问题讨论】:

  • 将&amp;gpioPort-&gt;IDR 转换为void* 应该没有问题(除非IDR 是const)。请复制并粘贴整个错误消息以及导致错误消息的代码。
  • 我还没有尝试过,但是如果你将构造函数设为一个模板,该模板接受一个指向模板类型的指针,然后在构造函数本身内部进行重新解释转换,会发生什么?如果您实际上使用不同的指针类型调用构造函数,这可能最终会扩大您的代码(如果它没有得到优化),但它可能会使您的调用看起来更清晰。或者,您可以考虑使用宏来缩短重新解释演员表的冗长。

标签: c++


【解决方案1】:

鉴于您提供的要求,是干净的方法。

我真的不明白你打算调用哪个构造函数(你没有明确你的目标),但不管怎样,reinterpret_cast 似乎最接近合理。

为了提醒您应该重新审视您的设计,并想出一个一开始就不需要这种转换的设计,它很冗长。因为我们不再生活在 1970 年代。 :)

但是,如果您坚持使用旧样式,则可以改用 C 样式转换:

Bitband foo((void*)&gpioPort->IDR, pin);

我恳求你不要这样做。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-22
    • 1970-01-01
    • 2017-05-20
    • 1970-01-01
    • 1970-01-01
    • 2010-12-28
    相关资源
    最近更新 更多