现在我是很反感C++这个语言

应当讲我现在是很不喜欢C++这个语言。

不仅仅语言复杂,这其实倒也无所谓,但问题是:C++是“一次编写,到处编链(生成),各种编链不过”,这就是C++的真正特点。这两天弄那个Teigha的CAD文件处理库,很装x闭源的库(所以C++也更应提倡开源,开源你去解决编译问题,闭源你就去解决可能永远解决不了的链接问题去吧)加一些很装x的例子程序写了一大坨,每种平台和配置都做一个包,总大小好几个G,拿下来去升级整合原先的调用程序,折腾了两天最后运行还通不过。这种情况只要遇到调用第三方库就总会遇到,而且每次遇到就算情况再相似都会无谓耗费大量时间。所以可以讲C++是一种开发效率非常低下的语言。无尽的咒骂。。。

最终除了很多冗繁的各类编译链接设置,这个问题最终是关于Unmanaged C++库(需要Windows CRT库)和C#应用程序side-by-side链接问题。

我的场景是(也是一个比较经典的):C#应用调用C++/CLI的类似Wrapper的库,而后者包含一些普通C++的动态连接库。而每次运行总会提示一些错误主要是“MSVCR90.DLL”无法找到,这个错误可以忽略,但之后的运行行为则不确定,一个旧版本的Teigha库可以正常运行(但也有一些报错),另一些版本则无法正常输出结果。有趣的是如果用一个C++/CLI做的managed的等价应用程序去调用,则不会有任何错误。一个简单的栈检查发现(也在意料之中),C++/CLI的应用的调用入口不存在managed和unmanaged的转移(C++在Windows(也许是几乎所有操作系统)下的确是名副其实的一等公民),这很有可能是造成这种执行行为差异的原因。

我尝试做了一个纯C/C++的DLL,然后其中做一些类似于那个Wrapper库做的简单的初始化工作(会导致那个MSVCR90.DLL无法找到错误)并暴露纯C接口(这是P/Invoke的规定),然后由C#去调用之,发现错误仍旧出现,说明这和C#对Native的互联操作方式无关。

而且解决这个问题的办法一般总是为C# App配置一个manifest。

有一些比较好的解答如:http://stackoverflow.com/questions/8039586/pinvoke-fails-because-of-dlls-dependent-on-other-sxs-dlls;只是这里要对文件版本等参数根据系统中存在的进行调整(要到Windows\winsxs目录下去搜索MSVCR90.DLL,然后根据文件夹名称的提示改写,名称可以从文件夹名字中看出,注意public key token是不变的(也在文件夹名中可看出)。另外这篇文章演示了如何根据项目配置读取特定的manifest(因为在debug下应当使用MSVCR90D.DLL)。

在根据步骤配置完manifest之后,我发现我仍然得到这些错误,非常恼怒,我尝试对C++ Wrapper工程也引入manifest,也无效。偶然我将配置改为Release,问题解决,运行一切正常,包括在那个DLL遗失错误之后的更严重运行错误也消失了(我估计对纯.NET接入的程序只能在Release模式不依赖那个debug版本CRT的情形下执行)。

然后我最后还得测试一下,如果去掉这个manifest会怎样,结果是运行失常,由此最终确定C# App需要这个新增的manifest。

另外在Teigha本身提供的.NET版本的库和实例工程中看,它的app并没有定制manifest,我估计原因是那个工程是在VS2008中,于是默认manifest或生成自动加入了这个VS2008 C++的运行时库路径。但它的功德是它只包含了Release版本,所以提示了我回到Release模式下去看看状况。

还是放松一下,让C#来平复一下被C++搞毛的心情,弄了一个用来分账单的小程序。多么优雅,写写这种代码被C++折去的寿命也能修补几年回来。

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 
  5 namespace BillSplitter  /* 20120130 */
  6 {
  7     class Program
  8     {
  9         struct UsageSegment : IComparable<UsageSegment>
 10         {
 11             public DateTime Start;  // inclusive
 12             public DateTime End;    // inclusive
 13             public decimal Units;   // number of persons
 14 
 15             public decimal TotalUnits
 16             {
 17                 get { return ((decimal)(End - Start).TotalDays + 1) * Units; }
 18             }
 19 
 20             public int CompareTo(UsageSegment other)
 21             {
 22                 if (End < other.Start) return -1;
 23                 return Start > other.End ? 1 : 0;
 24             }
 25         }
 26 
 27         class PersonalUsage
 28         {
 29             public DateTime MinDate { get; private set; }
 30             public DateTime MaxDate { get; private set; }
 31 
 32             public decimal TotalUnits
 33             {
 34                 get
 35                 {
 36                     if (_dirty)
 37                     {
 38                         _totalUnits = 0;
 39                         foreach (var seg in _segments)
 40                         {
 41                             _totalUnits += seg.TotalUnits;
 42                         }
 43                     }
 44                     return _totalUnits;
 45                 }
 46             }
 47 
 48             private readonly List<UsageSegment> _segments = new List<UsageSegment>();
 49 
 50             public PersonalUsage()
 51             {
 52                 MinDate = DateTime.MaxValue;
 53                 MaxDate = DateTime.MinValue;
 54             }
 55 
 56             public string[] GetDescription()
 57             {
 58                 var res = new string[_segments.Count];
 59                 var i = 0;
 60                 foreach (var seg in _segments)
 61                 {
 62                     res[i++] += string.Format("{0} Users: {1} to {2}",
 63                                          seg.Units, seg.Start.ToShortDateString(), seg.End.ToShortDateString());
 64                 }
 65                 return res;
 66             }
 67 
 68             public PersonalUsage Add(DateTime start, DateTime end, decimal units = 1)
 69             {
 70                 if (start > end)
 71                 {
 72                     throw new Exception("End date is not allowed to be earlier than start");
 73                 }
 74                 var newSeg = new UsageSegment { Start = start, End = end, Units = units };
 75                 var index = _segments.BinarySearch(newSeg);
 76                 if (index >= 0)
 77                 {
 78                     // overlapping
 79                     throw new Exception("Current implementation doesn't allow overlapping");
 80                 }
 81 
 82                 index = -index - 1;
 83                 _segments.Insert(index, newSeg);
 84 
 85                 if (start < MinDate) MinDate = start;
 86                 if (end > MaxDate) MaxDate = end;
 87 
 88                 _dirty = true;
 89 
 90                 return this;
 91             }
 92 
 93             private bool _dirty;
 94             private decimal _totalUnits;
 95         }
 96 
 97         class PersonDictionary : Dictionary<string, PersonalUsage>, IDictionary<string, PersonalUsage>
 98         {
 99             public new PersonalUsage this[string key]
100             {
101                 get
102                 {
103                     if (!ContainsKey(key))
104                     {
105                         base[key] = new PersonalUsage();
106                     }
107                     return base[key];
108                 }
109                 set { base[key] = value; }
110             }
111         }
112 
113         abstract class AbstractBalanceSheet
114         {
115             public abstract ICollection<string> PersonNames { get; }
116             public abstract decimal AmountsDueRounded(string name);
117 
118             public static AbstractBalanceSheet operator +(AbstractBalanceSheet a, AbstractBalanceSheet b)
119             {
120                 var result = new BalanceSheet();
121                 foreach (var p in a.PersonNames)
122                 {
123                     decimal value;
124                     if (b.PersonNames.Contains(p))
125                     {
126                         value = a.AmountsDueRounded(p) + b.AmountsDueRounded(p);
127                     }
128                     else
129                     {
130                         value = a.AmountsDueRounded(p);
131                     }
132                     result.SetAmountsDue(p, value);
133                 }
134 
135                 foreach (var p in b.PersonNames)
136                 {
137                     if (a.PersonNames.Contains(p))
138                     {
139                         continue;
140                     }
141                     var value = b.AmountsDueRounded(p);
142                     result.SetAmountsDue(p, value);
143                 }
144                 return result;
145             }
146         }
147 
148         private class BalanceSheet : AbstractBalanceSheet
149         {
150             private readonly IDictionary<string, decimal> PersonsUsage = new Dictionary<string, decimal>();
151 
152             public override ICollection<string> PersonNames
153             {
154                 get { return PersonsUsage.Keys; }
155             }
156 
157             public override decimal AmountsDueRounded(string name)
158             {
159                 return PersonsUsage[name];
160             }
161 
162             public void SetAmountsDue(string name, decimal value)
163             {
164                 PersonsUsage[name] = value;
165             }
166         }
167 
168         class UsageSplitter : AbstractBalanceSheet
169         {
170             public IDictionary<string, PersonalUsage> Persons { get; private set; }
171             public decimal TotalAmountDue { private get; set; }
172             public decimal SummedUnits { private get; set; }
173 
174             public override ICollection<string> PersonNames { get { return Persons.Keys; } }
175 
176             public UsageSplitter()
177             {
178                 Persons = new PersonDictionary();
179             }
180 
181             public void Recalculate()
182             {
183                 SummedUnits = Persons.Sum(x => x.Value.TotalUnits);
184             }
185 
186             public override decimal AmountsDueRounded(string name)
187             {
188                 return AmountsDueRounded(Persons[name]);
189             }
190 
191             public decimal AmountsDueAccurate(PersonalUsage p)
192             {
193                 var result = TotalAmountDue * p.TotalUnits / SummedUnits;
194                 return result;
195             }
196 
197             public decimal AmountsDueRounded(PersonalUsage p)
198             {
199                 var result = Math.Round(TotalAmountDue * p.TotalUnits * 100 / SummedUnits) / 100;
200                 return result;
201             }
202 
203             public void MakeSummary()
204             {
205                 Recalculate();
206                 decimal summedPayment = 0;
207                 var minDate = DateTime.MaxValue;
208                 var maxDate = DateTime.MinValue;
209                 Console.WriteLine("       Name         |               Description              | Usage | Payment ");
210                 foreach (var p in Persons)
211                 {
212                     var val = p.Value;
213                     var desc = val.GetDescription();
214                     var payment = AmountsDueRounded(val);
215                     summedPayment += payment;
216                     Console.WriteLine("{0,-20}|{1,-40}|{2,7}|{3,9:0.00}", p.Key, desc[0], val.TotalUnits, payment);
217                     for (var i = 1; i < desc.Length; i++)
218                     {
219                         Console.WriteLine("                    |{0,-40}|       |", desc[i]);
220                     }
221                     if (val.MinDate < minDate) minDate = val.MinDate;
222                     if (val.MaxDate > maxDate) maxDate = val.MaxDate;
223                 }
224                 Console.WriteLine("     [Total]        |{0,-40}|{1,7}|{2,9}",
225                     string.Format("Usage from {0} to {1}", minDate.ToShortDateString(), maxDate.ToShortDateString()),
226                     SummedUnits, summedPayment);
227             }
228         }
229 
230         static UsageSplitter ElectricitySummary()
231         {
232             var startDate = new DateTime(2012, 10, 26);
233             var endDate = new DateTime(2013, 1, 21);
234             var split = new UsageSplitter { TotalAmountDue = 431m };
235             var normal = new PersonalUsage();
236             normal.Add(startDate, endDate);
237 
238             split.Persons["Li"].Add(startDate, new DateTime(2012, 12, 9));
239             split.Persons["Hou"].Add(startDate, new DateTime(2012, 12, 9));
240             split.Persons["Sama"].Add(startDate, new DateTime(2012, 12, 5), 2);
241             split.Persons["Thomas"].Add(new DateTime(2013, 1, 7), endDate);
242             split.Persons["TonysNephew"].Add(new DateTime(2013, 1, 7), new DateTime(2013, 1, 13));
243 
244             split.Persons["Linc"].Add(startDate, new DateTime(2013, 1, 4), 2)
245                 .Add(new DateTime(2013, 1, 5), new DateTime(2013, 1, 11))
246                 .Add(new DateTime(2013, 1, 12), endDate, 2);
247 
248             split.Persons["Fulei"].Add(startDate, new DateTime(2013, 1, 9));
249             split.Persons["Jiale"].Add(startDate, new DateTime(2012, 12, 5));
250             split.Persons["Max"].Add(startDate, endDate);
251             split.Persons["Timmy"].Add(new DateTime(2013, 1, 7), endDate);
252 
253             split.MakeSummary();
254             return split;
255         }
256 
257         static UsageSplitter GasSummary()
258         {
259             var startDate = new DateTime(2012, 10, 26);
260             var endDate = new DateTime(2013, 1, 25);
261             var split = new UsageSplitter { TotalAmountDue = 263.75m };
262             var normal = new PersonalUsage();
263             normal.Add(startDate, endDate);
264 
265             split.Persons["Li"].Add(startDate, new DateTime(2012,12,9));
266             split.Persons["Hou"].Add(startDate, new DateTime(2012, 12, 9));
267             split.Persons["Sama"].Add(startDate, new DateTime(2012, 12, 5), 2);
268             split.Persons["Thomas"].Add(new DateTime(2013,1,7), endDate);
269             split.Persons["TonysNephew"].Add(new DateTime(2013, 1, 7), new DateTime(2013, 1, 13));
270 
271             split.Persons["Linc"].Add(startDate, new DateTime(2013, 1, 4), 2)
272                 .Add(new DateTime(2013, 1, 5), new DateTime(2013, 1, 11))
273                 .Add(new DateTime(2013, 1, 12), endDate, 2);
274 
275             split.Persons["Fulei"].Add(startDate, new DateTime(2013, 1, 9));
276             split.Persons["Jiale"].Add(startDate, new DateTime(2012, 12, 5));
277             split.Persons["Max"].Add(startDate, endDate);
278             split.Persons["Timmy"].Add(new DateTime(2013, 1, 7), endDate);
279 
280             split.MakeSummary();
281             return split;
282         }
283 
284         static void Main()
285         {
286             Console.WriteLine("== Electricity ==");
287             var se = ElectricitySummary();
288             Console.WriteLine("== Gas ==");
289             var sg = GasSummary();
290 
291             Console.WriteLine("== Summed ==");
292             var t = se + sg;
293             foreach (var p in t.PersonNames)
294             {
295                 Console.WriteLine("{0}: {1}DB", p, t.AmountsDueRounded(p));
296             }
297         }
298     }
299 }

现在有人说,C/C++节能环保,放在数据中心还是手持等大小设备上能减少功耗和热能输出。我了个去,生产力下来了,可维护性下来了,人的智慧和精力被拖垮了,一星半点的“环保”有什么用?C++就用在它所局限的地方那个就可以了,比如.NET的框架现在需要用它实现那没办法。但等到以后软硬件架构足够成熟,就应该将.NET对资源和运行时类型的管理都整合到硬件和操作系统架构中,并可以用这种更精简更符合人类思维的方式来同时进行软件甚至硬件的设计,这样真正做到思维、软件、硬件的相互推动,而不是局限于现在的老式的纯冯诺依曼架构。

我反正认为人的生命和创造力高于一切。C++如果再火起来是人类的悲哀,看来也不太可能。

 

posted @ 2012-10-31 21:55  quanben  阅读(483)  评论(0编辑  收藏  举报