separate_compilation.html 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
  2. <html>
  3. <head>
  4. <title>Guidelines for Authors of Boost Libraries Containing Separate Source</title>
  5. <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
  6. <LINK href="../boost.css" type="text/css" rel="stylesheet"></head>
  7. <body>
  8. <TABLE summary="Page header" id="Table1" cellSpacing="1" cellPadding="1" width="100%" border="0">
  9. <TR>
  10. <td vAlign="top" width="300">
  11. <h3><A href="../index.htm"><IMG height="86" alt="C++ Boost" src="../boost.png" width="277" border="0"></A></h3>
  12. </td>
  13. <TD width="353">
  14. <H1 align="center">Guidelines for Authors of Boost Libraries Containing Separate
  15. Source</H1>
  16. </TD>
  17. </TR>
  18. </TABLE>
  19. <BR>
  20. <HR>
  21. <P>These guidelines are designed for the authors of Boost libraries which have
  22. separate source that need compiling in order to use the library. Throughout,
  23. this guide refers to a fictitious "whatever" library, so replace all
  24. occurrences of "whatever" or "WHATEVER" with your own library's name when
  25. copying the examples.</P>
  26. <H2>Contents</H2>
  27. <dl class="index">
  28. <dt><A href="#source_changes">Changes Affecting Source Code</A>
  29. <dd>
  30. <dl class="index">
  31. <dt><A href="#abi">Preventing Compiler ABI Clashes</A> <DT><A href="#static_or_dynamic">Static
  32. or Dymanic Libraries</A>&nbsp; <dt><A href="#dlls">Supporting Windows Dll's</A> <dt>
  33. <a href="#auto-link">Automatic Library Selection and Linking with auto_link.hpp</a>
  34. </dt>
  35. </dl>
  36. <dt><A href="#build_changes">Changes Affecting the Build System</A>
  37. <dd>
  38. <dl class="index">
  39. <dt><A href="#jamfile">Creating the Library Jamfile</A> <dt><A href="#testing">Testing
  40. Auto-linking</A> </dt>
  41. </dl>
  42. <dt><A href="#copyright">Copyright</A></dt>
  43. </dl>
  44. <h2><A name="source_changes"></A>Changes Affecting Source Code</h2>
  45. <H3><A name="abi"></A>Preventing Compiler ABI Clashes</H3>
  46. <P>There are some compilers (mostly Microsoft Windows compilers again!), which
  47. feature a range of compiler switches that alter the ABI of C++ classes and
  48. functions. By way of example, consider Borland's compiler which has the
  49. following options:</P>
  50. <PRE>-b (on or off - effects enum sizes).
  51. -Vx (on or off - empty members).
  52. -Ve (on or off - empty base classes).
  53. -aX (alignment - 5 options).
  54. -pX (Calling convention - 4 options).
  55. -VmX (member pointer size and layout - 5 options).
  56. -VC (on or off, changes name mangling).
  57. -Vl (on or off, changes struct layout).
  58. </PRE>
  59. <P>These options are provided in addition to those affecting which runtime library
  60. is used (more on which later); the total number of combinations of options can
  61. be obtained by multiplying together the individual options above, so that gives
  62. 2*2*2*5*4*5*2*2 = 3200 combinations!
  63. </P>
  64. <P>The problem is that users often expect to be able to build the Boost libraries
  65. and then just link to them and have everything just plain work, no matter what
  66. their project settings are. Irrespective of whether this is a reasonable
  67. expectation or not, without some means of managing this issue, the user may
  68. well find that their program will experience strange and hard to track down
  69. crashes at runtime unless the library they link to was built with the same
  70. options as their project (changes to the default alignment setting are a prime
  71. culprit). One way to manage this is with "prefix and suffix" headers: these
  72. headers invoke compiler specific #pragma directives to instruct the compiler
  73. that whatever code follows was built (or is to be built) with a specific set of
  74. compiler ABI settings.</P>
  75. <P>Boost.config provides the macro BOOST_HAS_ABI_HEADERS which is set whenever
  76. there are prefix and suffix headers available for the compiler in use, typical
  77. usage in a header like this:</P>
  78. <PRE>#ifndef BOOST_WHATEVER_HPP
  79. #define BOOST_WHATEVER_HPP
  80. #include &lt;boost/config.hpp&gt;
  81. // this must occur after all of the includes and before any code appears:
  82. #ifdef BOOST_HAS_ABI_HEADERS
  83. # include BOOST_ABI_PREFIX
  84. #endif
  85. //
  86. // this header declares one class, and one function by way of examples:
  87. //
  88. class whatever
  89. {
  90. // details.
  91. };
  92. whatever get_whatever();
  93. // the suffix header occurs after all of our code:
  94. #ifdef BOOST_HAS_ABI_HEADERS
  95. # include BOOST_ABI_SUFFIX
  96. #endif
  97. #endif
  98. </PRE>
  99. <P>You can include this code in your library source files as well if you want,
  100. although you probably shouldn't need to:&nbsp;&nbsp;</P>
  101. <UL>
  102. <LI>
  103. If you <EM>don't</EM> use these in the library source files (but do in your
  104. library's headers) and the user attempts to compile the library source with a
  105. non-default ABI setting, then they will get compiler errors if there are any
  106. conflicts.</LI>
  107. <LI>
  108. If you <EM>do </EM>include them in both the library's headers and the library
  109. source files, then the code should always compile no matter what the compiler
  110. settings used, although the result might not match what the user was expecting:
  111. since we've forced the ABI back into default mode.</LI></UL>
  112. <H4>Rationale:</H4>
  113. <P>Without some means of managing this issue, users often report bugs along the
  114. line of "Your silly library always crashes when I try and call it" and so on.
  115. These issues can be extremely difficult and time consuming to track down, only
  116. to discover in the end that it's a compiler setting that's changed the ABI of
  117. the class and/or function types of the program compared to those in the
  118. pre-compiled library. The use of prefix/suffix headers can minimize this
  119. problem, although probably not remove it completely.</P>
  120. <H5>Counter Argument #1:</H5>
  121. <P>Trust the user, if they want 13-byte alignment (!) let them have it.</P>
  122. <H5>Counter Argument #2:</H5>
  123. <P>Prefix/suffix headers have a tendency to "spread" to other boost libraries -
  124. for example if boost::shared_ptr&lt;&gt; forms part of your class's ABI, then
  125. including prefix/suffix headers in your code will be of no use unless
  126. shared_ptr.hpp also uses them. Authors of header-only boost libraries may not
  127. be so keen on this solution - with some justification - since they don't face
  128. the same problem.</P>
  129. <H3><A name="static_or_dynamic"></A>Static or Dynamic Libraries</H3>
  130. <P>When the users runtime is dynamically linked the Boost libraries can be built
  131. either as dynamic libraries (.so's on Unix platforms, .dll's on Windows) or as
  132. static libraries (.a's on Unix, .lib's on Windows).&nbsp; So we have a choice
  133. as to which is supported by default:</P>
  134. <UL>
  135. <LI>
  136. On Unix platforms it typically makes no difference to the code: the user just
  137. selects in their makesfile which library they prefer to link to.</LI>
  138. <LI>
  139. On Windows platforms, the code has to be specially annotated to support DLL's,
  140. so we need to pick one option as the default and one as an alternative.</LI>
  141. <LI>
  142. On Windows platforms, we can inject special code to automatically select which
  143. library variant to link against: so again we need to decide which is to be the
  144. default (see the section on auto-linking below).</LI></UL>
  145. <P>The recomendation is to pick static linking by default.</P>
  146. <H4>Rationale:</H4>
  147. <P>There is no one policy that fits all here.
  148. </P>
  149. <P>The rationale for the current behaviour was inherited from Boost.Regex (and
  150. it's ancestor regex++): this library&nbsp;originally used dynamic linking by
  151. default whenever the runtime was dynamic. It's actually safer that way should
  152. you be using regex from a dll for example. However,&nbsp;this
  153. behavior&nbsp;brought a persistent stream of user complaints: mainly about
  154. deployment, all asking if static linking could be the default. After&nbsp;regex
  155. changed behavior the complaints stopped, and the author hasn't had one
  156. complaint about static linking by default being the wrong choice.</P>
  157. <P>Note that other libraries might need to make other choices: for example
  158. libraries that are intended to be used to implement dll pluggin's would like
  159. need to use dynamic linking in almost all cases.</P>
  160. <H3>Supporting Windows Dll's</H3>
  161. <p>On most Unix-like platforms no special annotations of source code are required
  162. in order for that source to be compiled as a shared library because all
  163. external symbols are exposed. However the majority of Windows compilers require
  164. that symbols that are to be imported or exported from a dll, be prefixed with
  165. __declspec(dllimport) or __declspec(dllexport). Without this mangling of source
  166. code, it is not possible to correctly build shared libraries on Windows
  167. (historical note - originally these declaration modifiers were required on
  168. 16-bit Windows where the memory layout for exported classes was different from
  169. that of "local" classes - although this is no longer an issue, there is still
  170. no way to instruct the linker to "export everything", it also remains to be
  171. seen whether 64-bit Windows will resurrect the segmented architecture that led
  172. to this problem in the first place. Note also that the mangled names of
  173. exported symbols are different from non-exported ones, so __declspec(dllimport)
  174. is required in order to link to code within a dll).</p>
  175. <p>In order to support the building of shared libraries on MS Windows your code
  176. will have to prefix all the symbols that your library exports with a macro
  177. (lets call it BOOST_WHATEVER_DECL) that your library will define to expand to
  178. either __declspec(dllexport) or __declspec(dllimport) or nothing, depending
  179. upon how your library is being built or used. Typical usage would look like
  180. this:</p>
  181. <pre>#ifndef BOOST_WHATEVER_HPP
  182. #define BOOST_WHATEVER_HPP
  183. #include &lt;boost/config.hpp&gt;
  184. #ifdef BOOST_HAS_DECLSPEC // defined in config system
  185. // we need to import/export our code only if the user has specifically
  186. // asked for it by defining either BOOST_ALL_DYN_LINK if they want all boost
  187. // libraries to be dynamically linked, or BOOST_WHATEVER_DYN_LINK
  188. // if they want just this one to be dynamically liked:
  189. #if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_WHATEVER_DYN_LINK)
  190. // export if this is our own source, otherwise import:
  191. #ifdef BOOST_WHATEVER_SOURCE
  192. # define BOOST_WHATEVER_DECL __declspec(dllexport)
  193. #else
  194. # define BOOST_WHATEVER_DECL __declspec(dllimport)
  195. #endif // BOOST_WHATEVER_SOURCE
  196. #endif // DYN_LINK
  197. #endif // BOOST_HAS_DECLSPEC
  198. //
  199. // if BOOST_WHATEVER_DECL isn't defined yet define it now:
  200. #ifndef BOOST_WHATEVER_DECL
  201. #define BOOST_WHATEVER_DECL
  202. #endif
  203. //
  204. // this header declares one class, and one function by way of examples:
  205. //
  206. class BOOST_WHATEVER_DECL whatever
  207. {
  208. // details.
  209. };
  210. BOOST_WHATEVER_DECL whatever get_whatever();
  211. #endif
  212. </pre>
  213. And then in the source code for this library one would use:
  214. <pre>
  215. //
  216. // define BOOST_WHATEVER SOURCE so that our library's
  217. // setup code knows that we are building the library (possibly exporting code),
  218. // rather than using it (possibly importing code):
  219. //
  220. #define BOOST_WHATEVER_SOURCE
  221. #include &lt;boost/whatever.hpp&gt;
  222. // class members don't need any further annotation:
  223. whatever::whatever() { }
  224. // but functions do:
  225. BOOST_WHATEVER_DECL whatever get_whatever()
  226. {
  227. return whatever();
  228. }
  229. </pre>
  230. <H4>Importing/exporting dependencies</H4>
  231. <P>As well as exporting your main classes and functions (those that are actually
  232. documented), Microsoft Visual C++ will warn loudly and often if you try to
  233. import/export a class whose dependencies are not also exported. Dependencies
  234. include: any base classes, any user defined types used as data members, plus
  235. all of the dependencies of your dependencies and so on. This causes particular
  236. problems when a dependency is a template class, because although it is
  237. technically possible to export these, it is not at all easy, especially if the
  238. template itself has dependencies which are implementation-specific details. In
  239. most cases it's probably better to simply suppress the warnings using:</P>
  240. <PRE>#ifdef BOOST_MSVC
  241. # pragma warning(push)
  242. # pragma warning(disable : 4251 4231 4660)
  243. #endif
  244. // code here
  245. #ifdef BOOST_MSVC
  246. #pragma warning(pop)
  247. #endif
  248. </PRE>
  249. <p>This is safe provided that there are no dependencies that are (template)
  250. classes with non-constant static data members, these really do need exporting,
  251. otherwise there will be multiple copies of the static data members in the
  252. program, and that's really really bad.
  253. </p>
  254. <p>Historical note: on 16-bit Windows you really did have to export all
  255. dependencies or the code wouldn't work, however since the latest Visual Studio
  256. .NET supports the import/export of individual member functions, it's a
  257. reasonably safe bet that Windows compilers won't do anything nasty - like
  258. changing the class's ABI - when importing/exporting a class.</p>
  259. <h4>Rationale:</h4>
  260. <p><EM>Why bother - doesn't the import/export mechanism take up more code that the
  261. classes themselves?</EM></p>
  262. <P>A good point, and probably true, however there are some circumstances where
  263. library code must be placed in a shared library - for example when the
  264. application consists of multiple dll's as well as the executable, and more than
  265. one those dll's link to the same Boost library - in this case if the library
  266. isn't dynamically linked and it contains any global data (even if that data is
  267. private to the internals of the library) then really bad things can happen -
  268. even without global data, we will still get a code bloating effect.
  269. Incidentally, for larger applications, splitting the application into multiple
  270. dll's can be highly advantageous - by using Microsoft's "delay load" feature
  271. the application will load only those parts it really needs at any one time,
  272. giving the impression of a much more responsive and faster-loading application.</P>
  273. <p><EM>Why static linking by default? </EM>
  274. </p>
  275. <P>In the worked example above, the code assumes that the library will be
  276. statically linked unless the user asks otherwise. Most users seem to prefer
  277. this (there are no separate dll's to distribute, and the overall distribution
  278. size is often significantly smaller this way as well: i.e. you pay for what you
  279. use and no more), but this is a subjective call, and some libraries may even
  280. only be available in dynamic versions (Boost.threads for example).</P>
  281. <h3><A name="auto-link"></A>Automatic Library Selection and Linking with <a href="../boost/config/auto_link.hpp">
  282. auto_link.hpp</a></h3>
  283. <p>Many Windows compilers ship with multiple runtime libraries - for example
  284. Microsoft Visual Studio .NET comes with 6 versions of the C and C++ runtime. It
  285. is essential that the Boost library that the user links to is built against the
  286. same C runtime as the program is built against. If that is not the case, then
  287. the user will experience linker errors at best, and runtime crashes at worst.
  288. The Boost build system manages this by providing different build variants, each
  289. of which is build against a different runtime, and gets a slightly different
  290. mangled name depending upon which runtime it is built against. For example the
  291. regex libraries get named as follows when built with Visual Studio .NET 2003:</p>
  292. <pre>boost_regex-vc71-mt-1_31.lib
  293. boost_regex-vc71-mt-gd-1_31.lib
  294. libboost_regex-vc71-mt-1_31.lib
  295. libboost_regex-vc71-mt-gd-1_31.lib
  296. libboost_regex-vc71-mt-s-1_31.lib
  297. libboost_regex-vc71-mt-sgd-1_31.lib
  298. libboost_regex-vc71-s-1_31.lib
  299. libboost_regex-vc71-sgd-1_31.lib
  300. </pre>
  301. <p>The difficulty now is selecting which of these the user should link his or her
  302. code to.</p>
  303. <p>In contrast, most Unix compilers typically only have one runtime (or sometimes
  304. two if there is a separate thread safe option). For these systems the only
  305. choice in selecting the right library variant is whether they want debugging
  306. info, and possibly thread safety.
  307. </p>
  308. <p>Historically Microsoft Windows compilers have managed this issue by providing a
  309. #pragma option that allows the header for a library to automatically select the
  310. library to link to. This makes everything automatic and extremely easy for the
  311. end user: as soon as they include a header file that has separate source code,
  312. the name of the right library build variant gets embedded in the object file,
  313. and as long as that library is in the linker search path, it will get pulled in
  314. by the linker without any user intervention.</p>
  315. <p>Automatic library selection and linking can be enabled for a Boost library by
  316. including the header &lt;boost/config/auto_link.hpp&gt;, after first defining
  317. BOOST_LIB_NAME and, if applicable, BOOST_DYN_LINK.</p>
  318. <pre>//
  319. // Automatically link to the correct build variant where possible.
  320. //
  321. #if !defined(BOOST_ALL_NO_LIB) &amp;&amp; !defined(BOOST_WHATEVER_NO_LIB) &amp;&amp; !defined(BOOST_WHATEVER_SOURCE)
  322. //
  323. // Set the name of our library, this will get undef'ed by auto_link.hpp
  324. // once it's done with it:
  325. //
  326. #define BOOST_LIB_NAME boost_whatever
  327. //
  328. // If we're importing code from a dll, then tell auto_link.hpp about it:
  329. //
  330. #if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_WHATEVER_DYN_LINK)
  331. # define BOOST_DYN_LINK
  332. #endif
  333. //
  334. // And include the header that does the work:
  335. //
  336. #include &lt;boost/config/auto_link.hpp&gt;
  337. #endif // auto-linking disabled
  338. </pre>
  339. <p>The library's user documentation should note that the feature can be disabled
  340. by defining either BOOST_ALL_NO_LIB or BOOST_WHATEVER_NO_LIB:</p>
  341. <P>If for any reason you need to debug this feature, the header
  342. &lt;boost/config/auto_link.hpp&gt; will output some helpful diagnostic messages
  343. if you first define BOOST_LIB_DIAGNOSTIC.</P>
  344. <H2><A name="build_changes"></A>Changes Affecting the Build System</H2>
  345. <H3><a name="build"></a><A name="jamfile"></A>Creating the library Jamfile</H3>
  346. <P>The Jamfile for building library "whatever" typically lives in
  347. boost-root/libs/whatever/build, start by defining the project root for the
  348. Jamfile:</P>
  349. <PRE>subproject libs/whatever/build ; </PRE>
  350. <P>Then add the static library build target (if supported):</P>
  351. <PRE>lib
  352. boost_whatever
  353. : # list all the sources for this
  354. library:
  355. ../src/whatever.cpp
  356. : # all build requirements go
  357. here. # the "common-variant-tag" rule ensures that the library will
  358. # be named according to the rules used by the install
  359. # and auto-link features:
  360. common-variant-tag
  361. # set include path for Boost headers:
  362. &lt;sysinclude&gt;$(BOOST_ROOT)
  363. :
  364. # list default build variants here
  365. debug release
  366. ; </PRE>
  367. <P>Then add the dll build target (if supported).&nbsp;&nbsp;In this case the build
  368. requirements section get an extra define: so that our sources know to export
  369. their own symbols (and import those from any other boost libs on which we may
  370. be dependent).&nbsp; We also restict shared library builds to dynamic-runtime
  371. build variants, if we don't do this then dll's linked against static runtimes
  372. are unlikely to function correctly (the dll will have a separate runtime from
  373. the executable using it, this generally causing problems with new and
  374. delete,&nbsp;as well as exception handling runtimes).</P>
  375. <PRE>dll
  376. boost_whatever
  377. : # list all the sources for this
  378. library:
  379. ../src/whatever.cpp
  380. : # all build requirements go
  381. here. # the "common-variant-tag" rule ensures that the library will
  382. # be named according to the rules used by the install
  383. # and auto-link features:
  384. common-variant-tag
  385. # tell our source that we're building (and maybe using) dll's:
  386. &lt;define&gt;BOOST_ALL_DYN_LINK=1
  387. # only build this for dynamic runtimes:
  388. &lt;runtime-link&gt;dynamic
  389. # set include path for Boost headers:
  390. &lt;sysinclude&gt;$(BOOST_ROOT)
  391. :
  392. # list default build variants here
  393. debug release
  394. ;
  395. </PRE>
  396. <P>Now add an install target so that Boost.Install can find this library to
  397. install:</P>
  398. <pre>install whatever lib
  399. : &lt;dll&gt;boost_whatever &lt;lib&gt;boost_whatever
  400. ;
  401. </pre>
  402. <P>Finally add a stage target that will copy the built libraries to a common
  403. sub-directory (boost-root/stage/lib):</P>
  404. <PRE>stage stage/lib : &lt;lib&gt;boost_whatever &lt;dll&gt;boost_whatever
  405. :
  406. # copy to a path rooted at BOOST_ROOT:
  407. &lt;locate&gt;$(BOOST_ROOT)
  408. # make sure the names of the libraries are correctly named:
  409. common-variant-tag
  410. # add this target to the "stage" and "all" psuedo-targets:
  411. &lt;target&gt;stage
  412. &lt;target&gt;all
  413. :
  414. debug release
  415. ;
  416. </PRE>
  417. <H3><A name="testing"></A>Testing Auto-linking</H3>
  418. <P>Testing the auto-link feature&nbsp;reasonable straightforward using
  419. the&nbsp;Boost.build system: we need to build the "whatever" library's test
  420. files without explicitly specifying the library to link to in the Jamfile, for
  421. example:</P>
  422. <PRE>subproject libs/whatever/test/auto-link-test ;
  423. # bring in the rules for testing
  424. import testing ;
  425. # start with a static linking version:
  426. run
  427. # sources
  428. ../whatever_test.cpp
  429. :
  430. : # input files
  431. : # requirements
  432. &lt;library-path&gt;../../../../stage/lib
  433. &lt;define&gt;BOOST_LIB_DIAGNOSTIC=1
  434. : # program name
  435. whatever_test
  436. ;
  437. # and then a dll linking version:
  438. run
  439. # sources
  440. ../whatever_test.cpp
  441. :
  442. : # input files
  443. : # requirements
  444. &lt;library-path&gt;../../../../stage/lib
  445. &lt;define&gt;BOOST_LIB_DIAGNOSTIC=1
  446. &lt;define&gt;BOOST_ALL_DYN_LINK=1
  447. &lt;runtime-link&gt;dynamic
  448. : # program name
  449. whatever_test_dll
  450. ;
  451. </PRE>
  452. <P>Please note however that this Jamfile will only build with compilers that do
  453. actually support auto-linking, so it should not be added to the regular
  454. regression tests.&nbsp; The Jamfile should also be built for all possible build
  455. variants, for the Microsoft / Borland compilers that means doing a:</P>
  456. <PRE>bjam -sBUILD="release debug &lt;threading&gt;multi/single &lt;runtime-link&gt;static/dynamic" test
  457. </PRE>
  458. <HR>
  459. <p><A name="copyright"></A>Revised
  460. <!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->
  461. 26 November, 2003<!--webbot bot="Timestamp" endspan i-checksum="39365" --></p>
  462. <p><i>© Copyright John Maddock&nbsp;1998-
  463. <!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%Y" startspan --> 2003<!--webbot bot="Timestamp" endspan i-checksum="746" --></i></p>
  464. <P><I>Distributed under the Boost Software License, Version 1.0. (See accompanying
  465. file <a href="../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or copy at <a href="http://www.boost.org/LICENSE_1_0.txt">
  466. http://www.boost.org/LICENSE_1_0.txt</a>)</I></P>
  467. <P><EM>The use of code snippets from this article does not require the reproduction
  468. of this copyright notice and license declaration; if you wish to provide
  469. attribution then please provide a link to this article.</EM></P>
  470. </body>
  471. </html>
粤ICP备19079148号