有一个与IE 9/10/11:如果你有一个SVG文件,其中一个已知的问题<svg>
元素指定的宽度和高度,然后使用缩放SVG图像width
和height
一个属性<img>
标签,IE不正确缩放图像.
我遇到过这个问题.我有一系列SVG标志图标.对于US标志图标,SVG对象写为:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="480" width="640">
<!-- elements to draw flag .. -->
</svg>
Run Code Online (Sandbox Code Playgroud)
我将SVG插入带有<img>
标记的HTML文档中,并将其缩小到20x15:
在Chrome 39上,SVG正确呈现如下:
但在IE10上,它呈现如下:
所以,这里似乎发生的事情是,即使IE10将<img>
元素的大小调整为20x15,它也不会缩小SVG的大小 - 所以我们最终只能看到标志图标的左上角,它看起来像一个纯蓝色的盒子.
好的......所以这似乎是一个已知问题,有文档化的解决方案.一种解决方案是简单地删除SVG文件中的所有width
和height
属性.这似乎有点危险,因为我不想搞砸实际的设计.如果你有很多SVG文件,这也有点麻烦 - 需要更多脚本来处理文件.
一个更好的解决方案是使用CSS专门针对IE10中的SVG元素,显然可以使用供应商特定的媒体查询:
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
img[src*=".svg"] {
width: 100%;
}
}
Run Code Online (Sandbox Code Playgroud)
好的,但我不明白这个解决方案.当我尝试上述操作时,IE10只是扩展了SVG的大小来填充整个父容器,这不是我想要的.好吧,也许我可以通过将SVG宽度设置为100%来强制IE扩展SVG,但随后约束父容器的大小.所以我把它包裹<img>
在宽度和高度为20x15的DIV中.但是......这只会导致与以前相同的问题:容器DIV固定为20x15,但SVG不会缩小 - 所以我们最终得到的是标志的左上角,如前所述:
......所以,我可能只是不了解这个解决方案.如何让IE10/11将标志图标缩小到20x15?
我目前正在学习如何使用autoconf
/ automake
toolchain.我似乎对这里的工作流程有一个大致的了解 - 基本上你有一个configure.ac
生成可执行configure
文件的脚本.configure
然后由最终用户执行生成的脚本以生成Makefile
s,因此可以构建/安装程序.
因此,典型最终用户的安装基本上是:
./configure
make
make install
make clean
Run Code Online (Sandbox Code Playgroud)
好的,现在我在这里困惑:
作为开发人员,我注意到自动生成的配置脚本有时不会运行,并且会出错:
config.status: error: cannot find input file: `somedir/Makefile.in'
Run Code Online (Sandbox Code Playgroud)
这混淆了我,因为我认为配置脚本应该产生的Makefile.in
.所以谷歌搜索一些答案,我发现这可以用autogen.sh
脚本修复,这基本上"重置"了autoconf
环境的状态.一个典型的autogen.sh
脚本将是这样的:
aclocal \
&& automake --add-missing \
&& autoconf
Run Code Online (Sandbox Code Playgroud)
好的.但作为终身用户,我终生下载了无数的tarball,我从来没有使用过autogen.sh
脚本.我所做的只是解压缩tarball,并执行通常的configure/make/make install/make clean例程.
但作为一个现在正在使用 的开发人员,除非你先运行,否则autoconf
它似乎configure
并没有真正运行autogen.sh
.所以我发现这非常令人困惑,因为我认为最终用户不应该运行autogen.sh
.
那么为什么我必须先运行autogen.sh
- 才能找到配置脚本Makefile.in
?为什么configure脚本不能简单地生成它?
通常,C++中引用计数智能ptr类的最广为人知的实现,包括标准std::shared_ptr
,使用原子引用计数,但不提供对同一智能ptr实例的原子访问.换句话说,多个线程可以安全地在shared_ptr
指向同一共享对象的单独实例上操作,但是多个线程不能安全地读取/写入同一shared_ptr
实例的实例而不提供某种同步,例如互斥或其他.
已经提出了shared_ptr
被称为" atomic_shared_ptr
" 的原子版本,并且已经存在初步实现.据推测,可以使用自旋锁或互斥锁轻松实现,但也可以实现无锁实现.atomic_shared_ptr
在研究了其中一些实现后,有一件事是显而易见的:实现无锁std::shared_ptr
是非常困难的,并且似乎需要这么多compare_and_exchange
操作才能让我质疑简单的自旋锁是否会实现更好的性能.
实现无锁引用计数指针如此困难的主要原因是因为在读取共享控制块(或共享对象本身,如果我们讨论的是侵入式共享指针)之间总是存在竞争,并修改引用计数.
换句话说,您甚至无法安全地读取引用计数,因为您永远不知道其他某个线程何时释放了引用计数所在的内存.
因此,通常,采用各种复杂策略来创建无锁版本.这里的实现看起来像是使用双引用计数策略,其中有"本地"引用计算并发访问shared_ptr
实例的线程数,然后是"共享"或"全局"引用,它们计算指向shared_ptr实例的数量到共享对象.
考虑到所有这些复杂性,我真的很惊讶地找到了Dobbs博士的文章,从2004年开始,(在C++ 11原子之前的方式)似乎无情地解决了整个问题:
http://www.drdobbs.com/atomic-reference-counting-pointers/184401888
看起来作者声称能够以某种方式:
"... [读取]指向计数器的指针,递增计数器,并以这样的方式返回指针 - 所有其他线程都不会导致错误的结果"
但我真的不明白他实际实现这一点的方式.他正在使用(非便携式)PowerPC指令(LL/SC原语lwarx
和stwcx
)将其关闭.
执行此操作的相关代码是他所谓的aIandF
"(原子增量和提取)",他将其定义为:
addr aIandF(addr r1){
addr tmp;int c;
do{
do{
tmp = *r1;
if(!tmp)break;
c = lwarx(tmp);
}while(tmp != *r1);
}while(tmp && !stwcx(tmp,c+1));
return tmp;
};
Run Code Online (Sandbox Code Playgroud)
显然,addr …
最近我一直在转换一些库来使用<system_error>
C++ 11中的工具.
我很难理解std::error_code
vs 的用例std::error_condition
.
注意,我理解不同之处 - stackoverflow上有很多问题可以解决这个问题.
基本区别在于std::error_code
应该表示特定于系统或平台的错误,而std::error_condition
API或用户界面应返回的是抽象错误.
好的 - 但是我很难理解为什么我们会std::error_code
在实践中使用它们.在我看来你要么:
被处理的是一个系统的具体错误报告机制(如说,errno
从POSIX调用返回或什么的,或者说,一个电话getsockopt
与SO_ERROR
在Linux上),你可以很容易地转换为std::error_condition
通过std::errc
枚举,这应该是便携.
使用用户定义的错误类别,它代表应用程序级别或业务逻辑错误,例如"无效的社会安全号码"或其他 - 这也是一个用例
std::error_condition
.
处理一些定义自己的错误报告机制的低级接口或库,例如OpenSSL,在这种情况下,您将直接使用特定于平台的错误机制.在这种情况下,您需要将这些错误转换或映射到std::error_code
.但是,如果您要解决将这些平台特定错误转换为通用类似错误的问题std::error_code
,为什么不转换为std::error_condition
?
此外,由于POSIX系统错误应该是可移植的,并且因为它们std::error_condition
通过std::errc
枚举一对一映射,所以我找不到任何用例std::error_code
.大多数Linux/UNIX系统调用set errno
,应该可以移植映射到std::error_condition
.
所以,我没有看到任何std::error_code
地方的任何用例.那么,我们想要使用的示例用例std::error_code
是std::error_condition
什么?
我对线程安全方面的要求有点困惑std::promise::set_value()
.
该标准说:
效果:以原子方式将值r存储在共享状态中并使该状态准备就绪
但是,它也说promise::set_value()
只能用于设置一次值.如果多次调用,std::future_error
则抛出a.因此,您只能设置一次承诺的值.
实际上,几乎每个教程,在线代码示例或实际用例都std::promise
涉及两个线程之间的通信通道,其中一个线程调用std::future::get()
,另一个线程调用std::promise::set_value()
.
我从来没有见过多个线程可能调用的用例std::promise::set_value()
,即使他们这样做,除了一个std::future_error
之外的所有线程都会引发异常.
那么为什么调用的标准命令std::promise::set_value()
是原子的呢?std::promise::set_value()
同时从多个线程调用的用例是什么?
编辑:
由于这里的最高投票答案并没有真正回答我的问题,我认为我所要求的不清楚.所以,澄清一下:我知道未来和承诺是什么以及它们如何运作.我的问题是,具体来说,为什么标准坚持认为std::promise::set_value()
必须是原子的?这是一个更为微妙的问题,而不是"为什么呼叫promise::set_value()
和呼叫之间不能有竞争future::get()
"?
事实上,这里的许多答案(错误地)都会回答原因是因为如果std::promise::set_value()
不是原子的,那么std::future::get()
可能会导致竞争条件.但是这是错误的.
避免竞争条件的唯一要求是std::promise::set_value()
必须具有与之发生的关系std::future::get()
- 换句话说,必须保证在std::future::wait()
返回时std::promise::set_value()
已完成.
这与std::promise::set_value()
原子本身是否完全正交.在使用条件变量的典型实现中,std::future::get()/wait()
将等待条件变量.然后,std::promise::set_value()
可以非原子地执行任何任意复杂的计算来设置实际值.然后它将通知共享条件变量(暗示具有释放语义的内存栅栏),std::future::get()
并将唤醒并安全地读取结果.
因此,std::promise::set_value()
本身并不需要原子来避免竞争条件 - 它只需要满足与之发生的关系std::future::get()
.
所以再一次,我的问题是:为什么 …
我的理解std::memory_order_acquire
和std::memory_order_release
如下:
获取意味着获取围栏之后出现的内存访问不能重新排序到围栏之前.
释放意味着在释放围栏之前出现的内存访问不能在围栏之后重新排序.
我不明白为什么特别是对于C++ 11原子库,获取围栏与加载操作相关联,而释放围栏与存储操作相关联.
为了澄清,C++ 11 <atomic>
库允许您以两种方式指定内存屏障:要么可以将fence指定为原子操作的额外参数,例如:
x.load(std::memory_order_acquire);
Run Code Online (Sandbox Code Playgroud)
或者您可以std::memory_order_relaxed
单独使用和指定围栅,例如:
x.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
Run Code Online (Sandbox Code Playgroud)
我不明白的是,鉴于上述获取和发布的定义,为什么C++ 11特别将获取与加载相关联,并与商店一起发布?是的,我已经看过许多示例,这些示例显示了如何使用获取/加载与发布/存储来在线程之间进行同步,但一般来说似乎是获取fences(防止语句后的内存重新排序)和发布的想法fences(在语句之前防止内存重新排序)与加载和存储的想法正交.
那么,为什么,例如,编译器不会让我说:
x.store(10, std::memory_order_acquire);
Run Code Online (Sandbox Code Playgroud)
我意识到我可以通过使用memory_order_relaxed
,然后单独的atomic_thread_fence(memory_order_acquire)
声明来完成上述操作,但同样,为什么我不能直接使用存储memory_order_acquire
?
一个可能的用例可能是,如果我想确保某些存储,比如在执行可能影响其他线程的其他语句之前x = 10
发生.
我试图了解如何正确编写AllocatorAware容器.
我的理解是propagate_on_container_move_assignment
typedef表示Allocator
当Container本身被移动分配时是否需要复制某种类型.
所以,既然我找不到任何这方面的例子,我自己的抨击就像下面这样:
给定容器类型Container
,Allocator
类型allocator_type
和内部allocator_type
数据成员m_alloc
:
Container& operator = (Container&& other)
{
if (std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value)
{
m_alloc = std::allocator_traits<allocator_type>::select_on_container_copy_construction(
other.m_alloc
);
}
return *this;
}
Run Code Online (Sandbox Code Playgroud)
它是否正确?
此外,另一个混乱的来源是嵌套的typedef propagate_on_container_move/copy_assignment
专门讨论赋值 ...但是构造函数呢?移动构造函数或AllocatorAware
容器的复制构造函数是否还需要检查这些typedef?我认为这里的答案是肯定的 ......意思是,我还需要写:
Container(Container&& other)
{
if (std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value)
{
m_alloc = std::allocator_traits<allocator_type>::select_on_container_copy_construction(
other.m_alloc
);
}
}
Run Code Online (Sandbox Code Playgroud) 我正在尝试创建一个类型特征类来确定特定类型是否T
可以通过<<
运算符流式传输std::ostream
.我正在使用简单的SFINAE技术.
最终,我试图评估替换失败的表达式是:
decltype(std::declval<std::ostream>() << std::declval<T>()) ;
Run Code Online (Sandbox Code Playgroud)
我的期望是,给定t
类型T
和std::ostream
实例的实例os
,如果表达式 os << t
格式错误,则应该发生替换失败.
但显然,无论类型如何,替代失败都不会发生在这里T
.即使我只是声明typedef
使用上面的decltype
表达式,在SFINAE的上下文之外,它很乐意编译,即使T
不能使用std::ostream
.
例如:
struct Foo { };
int main()
{
// This compiles fine using GCC 4.9.2
//
typedef decltype(
std::declval<std::ostream>() << std::declval<Foo>()
) foo_type;
}
Run Code Online (Sandbox Code Playgroud)
以上将使用GCC 4.9.2进行编译,这不是我预期的,因为<<
运算符没有重载以使用类型Foo
.当然,如果我说:
std::cout << Foo();
Run Code Online (Sandbox Code Playgroud)
...我收到编译器错误.那么为什么decltype
上面的表达式甚至可以编译呢?
有趣的是,我发现很多程序员错误地认为"无锁"只意味着"没有互斥的并发编程".通常,还存在一个相关的误解,即编写无锁代码的目的是为了获得更好的并发性能.当然,无锁的正确定义实际上是关于进度保证.无锁算法保证至少一个线程能够前进,无论其他线程正在做什么.
这意味着无锁算法永远不会有一个代码,其中一个线程依赖于另一个线程才能继续.例如,无锁代码不能具有线程A设置标志的情况,然后线程B在等待线程A取消设置标志时保持循环.这样的代码基本上实现了一个锁(或者我称之为伪装的互斥锁).
然而,其他情况更微妙,在某些情况下我真的无法确定算法是否符合无锁定的要求,因为"取得进步"的概念有时对我来说似乎是主观的.
其中一个例子是(备受好评的,afaik)并发库liblfds.我正在研究liblfds中多生产者/多消费者有界队列的实现 - 实现非常简单,但我无法确定它是否应该符合无锁定条件.
相关算法在lfds711_queue_bmm_enqueue.c
.Liblfds使用自定义原子和内存障碍,但算法很简单,我可以用段落左右来描述.
队列本身是一个有界的连续数组(ringbuffer).共享read_index
和write_index
.队列中的每个插槽都包含一个用户数据字段和一个sequence_number
值,它基本上类似于一个纪元计数器.(这避免了ABA问题).
PUSH算法如下:
write_index
write_index % queue_size
使用试图设置write_index
为的CompareAndSwap循环在队列中保留一个插槽write_index + 1
.sequence_index
通过使其等于来更新插槽write_index + 1
.实际的源代码使用自定义原子和内存障碍,因此为了进一步清楚这个算法,我简要地将它翻译成(未经测试的)标准C++原子以获得更好的可读性,如下所示:
bool mcmp_queue::enqueue(void* data)
{
int write_index = m_write_index.load(std::memory_order_relaxed);
for (;;)
{
slot& s = m_slots[write_index % m_num_slots];
int sequence_number = s.sequence_number.load(std::memory_order_acquire);
int difference = sequence_number - write_index;
if (difference == 0)
{
if (m_write_index.compare_exchange_weak(
write_index,
write_index + …
Run Code Online (Sandbox Code Playgroud) 我试图准确理解线程安全的原子引用计数是如何工作的,例如std::shared_ptr
.我的意思是,基本概念很简单,但我真的很担心减法加上如何delete
避免竞争条件.
Boost的这个教程演示了如何使用Boost原子库(或C++ 11原子库)实现原子线程安全的引用计数系统.
#include <boost/intrusive_ptr.hpp>
#include <boost/atomic.hpp>
class X {
public:
typedef boost::intrusive_ptr<X> pointer;
X() : refcount_(0) {}
private:
mutable boost::atomic<int> refcount_;
friend void intrusive_ptr_add_ref(const X * x)
{
x->refcount_.fetch_add(1, boost::memory_order_relaxed);
}
friend void intrusive_ptr_release(const X * x)
{
if (x->refcount_.fetch_sub(1, boost::memory_order_release) == 1) {
boost::atomic_thread_fence(boost::memory_order_acquire);
delete x;
}
}
};
Run Code Online (Sandbox Code Playgroud)
好的,所以我得到了一般的想法.但我不明白为什么以下情况不可能:
说refcount是当前的1
.
0
.1
.delete
托管对象指针.1
,访问托管对象指针... …