如何使用runtime获取该类中类别的属性名称

如何使用runtime获取该类中类别的属性名称,第1张

获取property list:

unsigned int count = 0;

objc_property_t properties = class_copyPropertyList([self class], &count);

获取property名:

const char propertyName = property_getName(properties[index])

使用KVC获取property值:

id propertyValue = [self valueForKey:name];

js获取元素的class名:

var className=documentgetElementsByClassName('names')[0];

jquery获取元素的class名:

var className = $('names')atrr('class');

相关概念

getElementsByClassName() 方法返回文档中所有指定类名的元素集合,作为 NodeList 对象。

NodeList 对象代表一个有顺序的节点列表。NodeList 对象 我们可通过节点列表中的节点索引号来访问列表中的节点(索引号由0开始)。

地球人都知道C++里有一个typeid *** 作符可以用来获取一个类型/表达式的名称

std::cout << typeid(int)name() << std::endl;

但是这个name()的返回值是

取决于编译器的

,在vc和gcc中打印出来的结果如下:

int // vc

i // gcc

一个稍微长一点的类型名称,比如:

class Foo {};

std::cout << typeid(Foo[10])name() << std::endl;

打出来是这个效果:

class Foo [10] // vc

A10_P3Foo // gcc

(话说gcc您的返回结果真是。。)

当然了,想在gcc里得到和微软差不多显示效果的方法也是有的,那就是使用

__cxa_demangle

char name = abi::__cxa_demangle(typeid(Foo[10])name(), nullptr, nullptr, nullptr);

std::cout << name << std::endl;

free(name);

显示效果:

Foo [10]

先不说不同编译器下的适配问题,来看看下面这个会打印出啥:

// vc

std::cout << typeid(const int&)name() << std::endl;

// gcc

char name = abi::__cxa_demangle(typeid(const int&)name(), nullptr, nullptr, nullptr);

std::cout << name << std::endl;

free(name);

显示效果:

int // vc

int // gcc

可爱的cv限定符和引用都被丢掉了==

如果直接在typeid的结果上加上被丢弃的信息,对于一些类型而言(如函数指针引用)得到的将不是一个正确的类型名称。

想要获得一个类型的完整名称,并且获得的名称必须要是一个正确的类型名称,应该怎样做呢?

一、如何检查C++中的类型

我们需要一个泛型类,用特化/偏特化机制静态检查出C++中的各种类型,并且不能忽略掉类型限定符(type-specifiers)和各种声明符(declarators)。

先来考虑一个最简单的类模板:

template <typename T>

struct check

{

//

};

假如在它的基础上特化,需要写多少个版本呢?我们可以稍微实现下试试:

template <typename T> struct check<T &>;

template <typename T> struct check<T const &>;

template <typename T> struct check<T volatile &>;

template <typename T> struct check<T const volatile &>;

template <typename T> struct check<T &&>;

template <typename T> struct check<T const &&>;

template <typename T> struct check<T volatile &&>;

template <typename T> struct check<T const volatile &&>;

template <typename T> struct check<T >;

template <typename T> struct check<T const >;

template <typename T> struct check<T volatile >;

template <typename T> struct check<T const volatile >;

template <typename T> struct check<T const>;

template <typename T> struct check<T volatile>;

template <typename T> struct check<T const volatile>;

template <typename T> struct check<T []>;

template <typename T> struct check<T const []>;

template <typename T> struct check<T volatile []>;

template <typename T> struct check<T const volatile []>;

template <typename T, size_t N> struct check<T [N]>;

template <typename T, size_t N> struct check<T const [N]>;

template <typename T, size_t N> struct check<T volatile [N]>;

template <typename T, size_t N> struct check<T const volatile [N]>;

//

这还远远没有完。有同学可能会说了,我们不是有伟大的宏嘛,这些东西都像是一个模子刻出来的,弄一个宏批量生成下不就完了。

实际上当我们真的信心满满的动手去写这些宏的时候,才发现适配上的细微差别会让宏写得非常痛苦(比如&和的差别,[]和[N]的差

别,还有函数类型、函数指针、函数指针引用、函数指针数组、类成员指针、……)。当我们一一罗列出需要特化的细节时,不由得感叹C++类型系统的复杂和纠

结。

但是上面的理由并不是这个思路的致命伤。

不可行的地方在于:我们可以写一个多维指针,或多维数组,类型是可以嵌套的。总不可能为每一个维度都特化一个模板吧。

不过正由于类型其实是嵌套的,我们可以用模板元编程的基本思路来搞定这个问题:

template <typename T> struct check<T const> : check<T>;

template <typename T> struct check<T volatile> : check<T>;

template <typename T> struct check<T const volatile> : check<T>;

template <typename T> struct check<T & > : check<T>;

template <typename T> struct check<T &&> : check<T>;

template <typename T> struct check<T > : check<T>;

//

一个简单的继承,就让特化变得simple很多。因为当我们萃取出一个类型,比如T ,之后的T其实是携带上了除之外所有其他类型信息的一个类型。那么把这个T再重复投入check中,就会继续萃取它的下一个类型特征。

可以先用指针、引用的萃取来看看效果:

#include <iostream>

template <typename T>

struct check

{

check(void) { std::cout << typeid(T)name(); }

~check(void) { std::cout << std::endl; }

};

#define CHECK_TYPE__(OPT) \

template <typename T> \

struct check<T OPT> : check<T> \

{ \

check(void) { std::cout << " "#OPT; } \

};

CHECK_TYPE__(const)

CHECK_TYPE__(volatile)

CHECK_TYPE__(const volatile)

CHECK_TYPE__(&)

CHECK_TYPE__(&&)

CHECK_TYPE__()

int main(void)

{

check<const volatile void const&>();

system("pause");

return 0;

}

输出结果(vc):

void const volatile const &

很漂亮,是不是?当然,在gcc里这样输出,void会变成v,所以gcc下面要这样写check模板:

template <typename T>

struct check

{

check(void)

{

char real_name = abi::__cxa_demangle(typeid(T)name(), nullptr, nullptr, nullptr);

std::cout << real_name;

free(real_name);

}

~check(void) { std::cout << std::endl; }

};

二、保存和输出字符串

我们可以简单的这样修改check让它同时支持vc和gcc:

template <typename T>

struct check

{

check(void)

{

# if defined(__GNUC__)

char real_name = abi::__cxa_demangle(typeid(T)name(), nullptr, nullptr, nullptr);

std::cout << real_name;

free(real_name);

# else

std::cout << typeid(T)name();

# endif

}

~check(void) { std::cout << std::endl; }

};

但是到目前为止,check的输出结果都是无法保存的。比较好的方式是可以像typeid(T)name()一样返回一个字符串。这就要求check能够把结果保存在一个std::string对象里。

当然了,我们可以直接给check一个“std::string&

out”类型的构造函数,但是这样会把输出的状态管理、字符的打印逻辑等等都揉在一起。因此,比较好的设计方法是实现一个output类,负责输出和维护

状态。我们到后面就会慢慢感觉到这样做的好处在哪里。

output类的实现可以是这样:

class output

{

bool is_compact_ = true;

template <typename T>

bool check_empty(const T&) { return false; }

bool check_empty(const char val)

{

return (!val) || (val[0] == 0);

}

template <typename T>

void out(const T& val)

{

if (check_empty(val)) return;

if (!is_compact_) sr_ += " ";

using ss_t = std::ostringstream;

sr_ += static_cast<ss_t&>(ss_t() << val)str();

is_compact_ = false;

}

std::string& sr_;

public:

output(std::string& sr) : sr_(sr) {}

output& operator()(void) { return (this); }

template <typename T1, typename T>

output& operator()(const T1& val, const T& args)

{

out(val);

return operator()(args);

}

output& compact(void)

{

is_compact_ = true;

return (this);

}

};

这个小巧的output类负责自动管理输出状态(是否增加空格)和输出的类型转换(使用std::ostringstream)。

上面的实现里有两个比较有意思的地方。

一是operator()的做法,采用了变参模板。这种做法让我们可以这样用output:

output out(str);

out("Hello", "World", 123, "!");

这种写法比cout的流 *** 作符舒服多了。

二是operator()和compact的返回值。当然,这里可以直接使用void,但是这会造成一些限制。

比如说,我们想在使用operator()之后马上compact呢?若让函数返回自身对象的引用,就可以让output用起来非常顺手:

output out(str);

outcompact()("Hello", "World", 123, "!")compact()("");

check的定义和CHECK_TYPE__宏只需要略作修改就可以使用output类:

template <typename T>

struct check

{

output out_;

check(const output& out) : out_(out)

{

# if defined(__GNUC__)

char real_name = abi::__cxa_demangle(typeid(T)name(), nullptr, nullptr, nullptr);

out_(real_name);

free(real_name);

# else

out_(typeid(T)name());

# endif

}

};

#define CHECK_TYPE__(OPT) \

template <typename T> \

struct check<T OPT> : check<T> \

{ \

using base_t = check<T>; \

using base_t::out_; \

check(const output& out) : base_t(out) { out_(#OPT); } \

};

为了让外部的使用依旧简洁,实现一个外敷函数模板是很自然的事情:

template <typename T>

inline std::string check_type(void)

{

std::string str;

check<T> { str };

return std::move(str);

}

int main(void)

{

std::cout << check_type<const volatile void const&>() << std::endl;

system("pause");

return 0;

}

处理ApplicationOnException时,如何获取具体类的名称呢处理ApplicationOnException时,怎么获取具体类的名称呢procedure TForm1FormCreate(Sender: TObject);beginApplicationOnException :=MyException;end;procedure TForm1MyException(Sender:TObject;E:Exception);beginShowMessage(EClassParent); // 这边怎样获取具体哪个类哪个方法的异常呢 end; 一般状况下,发布出去的程序是不带调试信息(Debug Information),所以无法取得详细信息(Call stack,单元名称,方法以及行数)。若要临时跟踪使用,可以编译一个带调试信息的exe,放上一个JVDebugHandle,设定好路径后,异常信息则可以输出到txt文件中,该控件提供了足够的信息让你了解问题所在。可能你想自己定义异常,在异常捕捉处,自己填充需要的信息。

如果你所说的对象的名字是否是指对象所属类型的名字,那么假设我有一个类:

class MyClass { }

现在我实例化这个类:

MyClass o = new MyClass();

透过从 SystemObjectGetType 继承的方法可以获得这个对象所属类的名字:

ConsoleWriteLine(oGetType()ToString());

输出将会是:MyClass。

以上就是关于如何使用runtime获取该类中类别的属性名称全部的内容,包括:如何使用runtime获取该类中类别的属性名称、js怎么获取元素的class名、如何在C++中获得完整的类型名称等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/9537977.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-29
下一篇 2023-04-29

发表评论

登录后才能评论

评论列表(0条)

保存