我刚才注意到C++标准说C和C++函数有不同的和不兼容的类型,即使它们的类型签名是相同的(更多信息请参阅这个问题).这意味着您在技术上不允许将C++函数传递给C函数pthread_create()
.
我很好奇是否有任何平台在使用两个ABI实际上是不同的(除了显而易见的名称差异).具体来说,有没有人知道这个C++程序无法编译和运行的任何平台?
#include <assert.h>
extern "C" int run(int (*f)(int), int x) { return f(x); }
int times2(int x) { return x * 2; }
int main(int argc, char *argv[]) {
int a = times2(argc);
// This is undefined behavior according to C++ because I am passing an
// "extern C++" function pointer to an "extern C" function.
int b = run(×2, argc);
assert(a == b);
return a;
}
Run Code Online (Sandbox Code Playgroud) 假设您有一个C++类,如:
class Foo {
public:
virtual ~Foo() {}
virtual DoSomething() = 0;
};
Run Code Online (Sandbox Code Playgroud)
C++编译器将调用转换为vtable查找:
Foo* foo;
// Translated by C++ to:
// foo->vtable->DoSomething(foo);
foo->DoSomething();
Run Code Online (Sandbox Code Playgroud)
假设我正在编写JIT编译器,并且我想获取类Foo的特定实例的DoSomething()函数的地址,因此我可以生成直接跳转到它的代码,而不是执行表查找和间接分支.
我的问题是:
有没有任何标准的C++方法来做到这一点(我几乎可以肯定答案是否定的,但是为了完整起见,我想问一下).
有没有任何独立于远程编译器的方法,比如有人实现了提供访问vtable的API的库?
如果他们能够工作,我会完全打开黑客.例如,如果我创建了自己的派生类并且可以确定其DoSomething方法的地址,我可以假设vtable是Foo的第一个(隐藏)成员并搜索其vtable直到找到我的指针值.但是,我不知道获得这个地址的方法:如果我写的话,&DerivedFoo::DoSomething
我得到一个指向成员的指针,这是完全不同的东西.
也许我可以将指向成员的指针转换为vtable偏移量.当我编译以下内容时:
class Foo {
public:
virtual ~Foo() {}
virtual void DoSomething() = 0;
};
void foo(Foo *f, void (Foo::*member)()) {
(f->*member)();
}
Run Code Online (Sandbox Code Playgroud)
在GCC/x86-64上,我得到了这个程序集输出:
Disassembly of section .text:
0000000000000000 <_Z3fooP3FooMS_FvvE>:
0: 40 f6 c6 01 test sil,0x1
4: 48 89 74 24 e8 mov QWORD PTR [rsp-0x18],rsi
9: 48 89 …
Run Code Online (Sandbox Code Playgroud) 许多SSE"mov"指令指定它们正在移动浮点值.例如:
为什么这些指令只是说它们移动32位或64位值?如果它们只是移动位,为什么指令指定它们是浮点值?当然,无论你是否将这些位解释为浮点,它们都能工作吗?
假设我正在使用一个C API,它允许您注册带有void*
闭包的回调:
void register_callback(void (*func)(void*), void *closure);
Run Code Online (Sandbox Code Playgroud)
在C++中,拥有更强大的类型是很好的,void*
所以我想创建一个包装器,让我注册强类型的C++回调:
template <typename T, void F(T*)>
void CallbackWrapper(void *p) {
return F(static_cast<T*>(p));
}
void MyCallback(int* param) {}
void f(void *closure) {
register_callback(CallbackWrapper<int, MyCallback>, closure);
}
Run Code Online (Sandbox Code Playgroud)
这没关系.这个解决方案的一个很好的属性是它可以将我的回调内联到包装器中,所以这个包装方案没有开销.我认为这是一个要求.
但是如果我能让API看起来更像这样会很好:
void f2() {
RegisterCallback(MyCallback, closure);
}
Run Code Online (Sandbox Code Playgroud)
我希望通过推断模板参数来实现上述目的.但我无法弄清楚如何让它发挥作用.我到目前为止的尝试是:
template <typename T>
void RegisterCallback(void (*f)(T*), T* closure) {
register_callback(CallbackWrapper<T, f>, closure);
}
Run Code Online (Sandbox Code Playgroud)
但这不起作用.任何人都有一个神奇的咒语,可以使f2()
上面的工作,同时保持零开销性能特征?我想要一些适用于C++ 98的东西.
是否可以在同一个内存位置执行原子操作和非原子操作?
我问不是因为我真的想这样做,而是因为我试图理解C11/C++ 11内存模型.他们定义了一个"数据竞争",如下所示:
程序的执行包含数据竞争,如果它在不同的线程中包含两个冲突的动作,其中至少有一个不是原子的,并且都不会在另一个之前发生.任何此类数据争用都会导致未定义的行为.- C11 §5.1.2.4P25,C++ 11 §1.10 p21基因
它的"至少有一个不是原子的"是困扰我的部分.如果不可能混合原子和非原子操作,它只会说"在一个非原子的物体上".
我看不到对原子变量执行非原子操作的任何直接方式. std::atomic<T>
在C++中,没有使用非原子语义定义任何操作.在C中,原子变量的所有直接读/写似乎都被转换为原子操作.
我想memcpy()
和其他直接内存操作可能是对原子变量执行非原子读/写的方式吗?即.memcpy(&atomicvar, othermem, sizeof(atomicvar))
?但这是否定义为行为?在C++中,std::atomic
是不可复制的,那么它是否会memcpy()
在C或C++中定义它的行为?
原子变量的初始化(无论是通过构造函数还是atomic_init()
)被定义为不是原子的.但这是一次性操作:您不允许第二次初始化原子变量.放置新的或显式的析构函数调用也可能不是原子的.但是在所有这些情况下,似乎不会定义行为,因为有一个可能在未初始化值上运行的并发原子操作.
对非原子变量执行原子操作似乎完全不可能:C和C++都没有定义任何可以对非原子变量进行操作的原子函数.
那么这里的故事是什么?是真的memcpy()
,或初始化/破坏,还是其他什么?
以下是关于辅助扩展的GNU Make手册部分的示例(略微简化):
foo_SRCS := bar.c baz.c
.SECONDEXPANSION:
# $$@ expands to the target ("foo" in this case)
foo: $$(patsubst %.c,%.o,$$($$@_SRCS))
Run Code Online (Sandbox Code Playgroud)
这很好用; 它构建bar.o
和baz.o
:
cc -c -o bar.o bar.c
cc -c -o baz.o baz.c
Run Code Online (Sandbox Code Playgroud)
但是,如果我只略微调整这个例子,patsubst
停止工作:
all: foo.a
foo_SRCS := bar.c baz.c
.SECONDEXPANSION:
# $$* expands to the stem of the match ("foo" in this case).
%.a: $$(patsubst %.c,%.o,$$($$*_SRCS))
ar rcs $@ $^
Run Code Online (Sandbox Code Playgroud)
它不再建造bar.o
和baz.o
,而是使用*.c
直接文件作为先决条件!
ar rcs foo.a …
Run Code Online (Sandbox Code Playgroud) 假设我为自己定义了一个隐式转换函数:
#include <iostream>
class Foo {
public:
operator Foo() {
std::cout << "wha??\n";
return Foo();
}
};
void f(Foo f) {}
int main() {
Foo foo;
f(foo);
}
Run Code Online (Sandbox Code Playgroud)
我为什么定义这个?好吧,我永远不会直接写它,但它可能发生在我写的模板类中的模板实例化.如果发生这种情况,我希望定义转换函数实际上是无操作,并且实际调用是不可能的.
上面的程序没有打印(这很棒).是否存在实际调用此转换函数的任何情况?
Python文档声称以下内容不适用于"某些平台或编译器":
int foo(int); // Defined in another translation unit.
struct X { int (*fptr)(int); } x = {&foo};
Run Code Online (Sandbox Code Playgroud)
具体来说,Python文档说:
我们想把它分配给tp_new插槽,但是为了便携起见,我们不能,在某些平台或编译器上,我们不能用另一个C模块中定义的函数静态初始化一个结构成员,所以,相反,我们将在调用PyType_Ready()之前在模块初始化函数中分配tp_new槽.- http://docs.python.org/extending/newtypes.html
以上标准C89和/或C99?什么编译器具体无法处理上述问题?
调用mlock()
某个内存和调用shmctl(SHM_LOCK)
同一内存有什么区别?
这些是我能确定的唯一区别:
mlock()
保证在返回时加载所有锁定的页面。 shmctl(SHM_LOCK)
防止交换,但不会主动加载非驻留页面。shmctl(SHM_LOCK)
只能用于共享内存段。shmctl(SHM_LOCK)
SHM_LOCKED
在共享内存段的权限上设置一个额外的标志 ( )。还有其他区别吗?特别是,是否有任何理由不在mlock()
共享内存段上使用?
该阵营v0.12发布公告包括以下内容:
新功能:
Run Code Online (Sandbox Code Playgroud)* React.addons.batchedUpdates added to API for hooking into update cycle
但是,我找不到此API的任何文档.它的目的是什么?
具体来说,它有相同的机会Ember.run()
吗?