从类型和字符串返回指针

时间:2014-04-28 17:20:06

标签: c++ qt

如何从其名称字符串返回已知类型的(现有)指针?假设我创建了一些名为map<string, double>的{​​{1}}和一个名为map1的{​​{1}}。我想编写一个函数,它将从vector<string> vec1map1 "map1"返回vec1(以及另外一个指定每个类型的参数)。< / p>

这可能吗?

我的最终目标是以"vec1"形式获取QWidget名称,因为我与其中的大量合作,但答案应该与框架无关, ?

2 个答案:

答案 0 :(得分:0)

您需要执行某种动态调度。要做到这一点,你可以简单地从你提出的建议开始:

enum class types { A = 0, B = 1 };
void (*handlers[])(void*) = { &HandleA, &HandleB };

::std::unordered_map<::std::string, ::std::tuple<types, void*>> registry;

现在剩下的就是执行查找:

void lookup(::std::string const& name)
{
    auto& t = registry.at(name);
    handlers[static_cast<size_t>(::std::get<0>(t))](::std::get<1>(t));
}

处理程序的自动参数转换

处理程序都采用void*类型的参数 - 这可以通过添加一个小模板魔术来处理:

template<typename T, void(*f)(T*)>
void handle(void* arg)
{
    f(static_cast<T*>(arg));
}

void (*handlers[])(void*) = { &handle<A, &HandleA>, &handle<B, &HandleB> };

现在,原型是例如void HandleA(A*)

简单地将对象添加到注册表

使用当前代码,您可以将对象添加到注册表中,如下所示:

A a;
registry.emplace("A #1", ::std::make_tuple(types::A, &a));

虽然这很有效,但我们想做一些更优雅的事情。让我们首先将enum class types更改为也知道我们要代表它的类型的东西:

template<typename T> struct types;
template<> struct types<A> { static const size_t id = 0; };
template<> struct types<B> { static const size_t id = 1; };

当然,现在我们需要修复注册表类型:

::std::unordered_map<::std::string, ::std::tuple<size_t, void*>> registry;

最后我们可以提供一个简单的插入函数:

template<typename T>
void insert(::std::string const& name, T* object)
{
    registry.emplace(name, ::std::make_tuple(types<T>::id, static_cast<void*>(object)));
}

最终用法示例

A a;
insert("A #1", &a);
lookup("A #1");

答案 1 :(得分:0)

元对象系统已经处理了这个问题,因此答案将是特定于框架的,因为您通常需要一个代码生成器来获取有关C ++类型的元数据,这些元数据不可用。

QLineEdit * ed = ...;
ed->setObjectName("myObject");

... elsewhere in the code

foreach(QWidget * w, QCoreApplication::allWidgets()) {
  // Lookup by name
  if (w->objectName() == "myObject") {
    ...
  }
  // Lookup by type
  if (qobject_cast<QLineEdit*>(w)) {
    ...
  }
}

如果您想加快查找速度,并且对象具有唯一的名称:

class Widgets {
  typedef QMap<QString, QPointer<QWidget>> Data;
  mutable Data m_map;
public:
  Widgets() {
    foreach(QWidget * w, QCoreApplication::allWidgets()) {
      if (w->objectName().isEmpty()) continue;
      m_map.insert(w->objectName(), w);
    }
  }
  QWidget * lookupWidget(const QString & name) const {
    Data::iterator it = m_map.find(name);
    if (it == m_map.end()) return nullptr;
    QWidget * w = it->data();
    if (!w) m_map.erase(it); // The widget doesn't exist anymore
    return w;
  }
  template <typename T> T * lookup(const QString & name) const {
    return qobject_cast<T*>(lookupWidget(name));
  }
  void setName(QWidget * w, const QString & name) {
    Q_ASSERT(! name.isEmpty());
    w->setObjectName(name);
    m_map.insert(name, w);
  }
};

在您的代码中,使用widgets->setName()代替setObjectName

如果你想查询名称和类型,只要它们都是不同的类型,重复的名称就可以了:

class Widgets2 {
  typedef QPair<QString, QString> Key;
  typedef QMap<Key, QPointer<QWidget>> Data;
  mutable Data m_map;
  static Key keyFor(QWidget * w) {
    return qMakePair(w->objectName(),
      QString::fromLatin1(w->metaObject()->className()));
public:
  Widgets2() {
    foreach(QWidget * w, QCoreApplication::allWidgets()) {
      if (w->objectName().isEmpty()) continue;
      m_map.insert(keyFor(w), w);
    }
  }
  QWidget * lookupWidget(const QString & name, const QString & type) const {
    Data::iterator it = m_map.find(qMakePair(name, type));
    if (it == m_map.end()) return nullptr;
    QWidget * w = it->data();
    if (!w) m_map.erase(it); // The widget doesn't exist anymore
    return w;
  }
  template <typename T> T * lookup(const QString & name) const 
  {
    return qobject_cast<T*>(lookupWidget(name, 
      QString::fromLatin1(T::staticMetaObject.className())));
  }
  void setName(QWidget * w, const QString & name) {
    Q_ASSERT(! name.isEmpty());
    w->setObjectName(name);
    m_map.insert(keyFor(w), w);
  }
};

查找的工作原理如下:

widgets2->lookup<QLineEdit>("myObject")->setText("foo");

我利用QObjectQPointer使小部件删除安全的小部件注册表安全 - 你不会得到一个悬空指针。

如果您愿意,也可以跟踪对象名称更改:QObject发出objectNameChanged信号。

所有这些当然是对代码设计损坏的可怕破解。您需要这一事实意味着您将业务逻辑与GUI紧密耦合。您应该使用某种模型 - 视图架构。