当时我的任务是编写一个多平台的文档转换器。
当时我受雇于一家开发电子书app的小公司。该软件由AdobeInDesign的插件、OSX出版管理软件、iOS电子阅读器应用程序和同级别的Android应用程序组成。
像大多数程序员一样,我是以我的iOS和OSX的开发能力被聘用的。但我没想到的是,我在一年内就成为了一名C#、Typescript和TSQL的开发者——我一边工作一边学习了业务需要的各种技能。6个月后,噩梦般的任务来了。
我们认为你可以做下这个
做什么?
一个打开Adobe的文件格式,然后生成我们的文件的APP
啊,做一个后台进程的ASP.NET前端,就像我们的网络客户端?”
我们希望它能在设备上运行
什么设备?所有的?
对,安卓、iOS和WidowsMobile。。哦,最好还能用浏览器直接打开。
总有一天,你会痛苦地发现自己的工资很少,甚至远未达到你最初的预期。对此我有两个深刻的记忆。
第一次,是我花了太多的时间来解释和演示“可组合的获取方法对全局状态的好处”,而我的同事却推翻了我们的重构,增加了更多的全局变量,结果过段时间又抱怨说当有两个快速连续的调用时,数据被覆盖了。我真的无法和这个人共事,他根本不懂编程。
第二次,是我的老板让我写一个跨平台的嵌入式C++库,把一种准开放的文档格式转换成另一种用C#开发的格式。
哇,我刚开始真的非常喜欢这个挑战。这是我在过去12年迄今为止最有趣的一次编程。这是一个噩梦般的任务——那种基本没有可能完成的任务,一种专有的格式,除了让它成功运行之外没有其他目标,也没有什么成就感——但我做到了。我成功了,而且这段经历最后对我的职业生涯来说是非常有价值。
但是(总会有一个但是)我有一个为难了两天的bug,就是一直无法解决。
在过去的6年里,C++的版本发生了巨大的变化。C++11到现在的C++14的改进使一些微不足道的事情所需要的代码变得更加简单。Copyelision/RVO、移动语义和自动类型使工作变得轻而易举。然而,就在我以为我已经把我的三条规则构造函数掌握得很好的时候,出现像vec.push_back(page)这样简单的函数在std::vectorpush_back/emplace_back中运行时丢出奇怪的异常。
那天不是一个令人舒适的早晨。
我怎么也想不明白,为什么容器不能添加一个page的智能指针。这是我几周前就已经在运行的模板代码。没有对我复制的内容进行修改。我没有使用自己的移动魔法。它只是添加了一个智能指针。一个简单的智能指针。
我注释了一些代码,并最终将其改成了:
shared_ptrPageRefpage(newPageRef());pages.push_back(page);
我甚至把main()都注释掉了,用上面的内容来替代。在这一问题上,我的心情变得非常低落丧气,重构的东西越来越多。我花了整整一天半的时间来修复它。即使是google上也找不到帮我解决STL容器内部的某个抽象异常的方法。
我当时用的是VisualStudio。我知道MS的STL实现在过去是多么的不可靠,但我们现在是在StephanT.Lavavej时代,MS的实现是坚如磐石。一定是我的代码在某个地方有错误。
我又回头看了看我上星期五写的代码。那些都是无害的代码。没有哪行代码是我没有测试和彻底调试过的。最后,我终于恍然大悟。我记得我最后编辑过的代码是文件系统扫描和图像分析器的头文件。在某个星期五,在那天下午5点40分,是不是
我打开了JPG图像解析头文件,它的开头是:
#pragmapack(push,1)structJpgWord{JpgByte_tHi;JpgByte_tLo;inlineJpgWord_tValue()const{return(Hi8)
Lo;}};
第一行是告诉编译器将我的结构对准1个字节的边界,而不是8个字节的边界,删掉没必要的填充。这让我可以直接从流中映射结构。我已经预感到要发生什么。我用鼠标拉到文件的底部,没猜错,就是少了什么东西。我都崩溃了,真的是个万年一遇的人才才能写出的代码。
代码原本的最后一行不在那里。我在给文件头添加另一个结构时不小心把它给覆盖了。
而且我还没有弹出:
#pragmapack(pop)
那两天的痛苦我现在想起来还全身发麻,我都开始怀疑我选择的编程语言和编程能力,怀疑我自己是否还适合选择走编程这条路。
这是我当程序员最至暗的时刻,而且仅仅只是因为头文件中的一个编译器指令。
很高兴大家能看完我的分享,