Commit 7a7064df4edd519b93de65b6cade23d60d4365d9

Authored by Henry Fredrick Schreiner
Committed by Henry Schreiner
1 parent eed23836

Adding optional, refactor single file

include/CLI/CLI.hpp
@@ -10,6 +10,8 @@ @@ -10,6 +10,8 @@
10 10
11 #include "CLI/Macros.hpp" 11 #include "CLI/Macros.hpp"
12 12
  13 +#include "CLI/Optional.hpp"
  14 +
13 #include "CLI/StringTools.hpp" 15 #include "CLI/StringTools.hpp"
14 16
15 #include "CLI/Error.hpp" 17 #include "CLI/Error.hpp"
include/CLI/Optional.hpp 0 → 100644
  1 +#pragma once
  2 +
  3 +// Distributed under the 3-Clause BSD License. See accompanying
  4 +// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
  5 +
  6 +#include <sstream>
  7 +
  8 +#include "CLI/Macros.hpp"
  9 +
  10 +// [CLI11:verbatim]
  11 +#ifdef __has_include
  12 +#if defined(CLI11_CPP17) && __has_include(<optional>)
  13 +#include <optional>
  14 +#define CLI11_OPTIONAL
  15 +namespace CLI {
  16 +using std::experimental::optional;
  17 +} // namespace CLI
  18 +#elif defined(CPP11_CPP14) && __has_include(<experimental/optional>)
  19 +#include <experimental/optional>
  20 +#define CLI11_OPTIONAL
  21 +namespace CLI {
  22 +using std::optional;
  23 +} // namespace CLI
  24 +#endif
  25 +#endif
  26 +// [CLI11:verbatim]
  27 +
  28 +namespace CLI {
  29 +
  30 +#ifdef CLI11_OPTIONAL
  31 +
  32 +template <typename T> std::istream &operator>>(std::istream &in, optional<T> &val) {
  33 + T v;
  34 + in >> v;
  35 + val = v;
  36 + return in;
  37 +}
  38 +
  39 +#endif
  40 +
  41 +} // namespace CLI
scripts/MakeSingleHeader.py
@@ -5,60 +5,110 @@ from __future__ import print_function, unicode_literals @@ -5,60 +5,110 @@ from __future__ import print_function, unicode_literals
5 import os 5 import os
6 import re 6 import re
7 import argparse 7 import argparse
  8 +import operator
  9 +from copy import copy
8 from subprocess import check_output, CalledProcessError 10 from subprocess import check_output, CalledProcessError
  11 +from functools import reduce
9 12
10 includes_local = re.compile(r"""^#include "(.*)"$""", re.MULTILINE) 13 includes_local = re.compile(r"""^#include "(.*)"$""", re.MULTILINE)
11 includes_system = re.compile(r"""^#include \<(.*)\>$""", re.MULTILINE) 14 includes_system = re.compile(r"""^#include \<(.*)\>$""", re.MULTILINE)
  15 +verbatim_tag_str = r"""
  16 +^ # Begin of line
  17 +[^\n^\[]+ # Some characters, not including [ or the end of a line
  18 +\[ # A literal [
  19 +[^\]^\n]* # Anything except a closing ]
  20 +CLI11:verbatim # The tag
  21 +[^\]^\n]* # Anything except a closing ]
  22 +\] # A literal ]
  23 +[^\n]* # Up to end of line
  24 +$ # End of a line
  25 +"""
  26 +verbatim_all = re.compile(verbatim_tag_str + "(.*)" + verbatim_tag_str,
  27 + re.MULTILINE | re.DOTALL | re.VERBOSE)
  28 +
  29 +DIR = os.path.dirname(os.path.abspath(__file__))
  30 +
  31 +class HeaderFile(object):
  32 + TAG = "Unknown git revision"
  33 +
  34 + def __init__(self, base, inc):
  35 + with open(os.path.join(base, inc)) as f:
  36 + inner = f.read()
12 37
13 -DIR = os.path.dirname(os.path.abspath(__file__)) # Path(__file__).resolve().parent  
14 -BDIR = os.path.join(os.path.dirname(DIR), 'include') # DIR.parent / 'include' 38 + # add self.verbatim
  39 + if 'CLI11:verbatim' in inner:
  40 + self.verbatim = ["\n\n// Verbatim copy from {}".format(inc)]
  41 + self.verbatim += verbatim_all.findall(inner)
  42 + inner = verbatim_all.sub("", inner)
  43 + else:
  44 + self.verbatim = []
15 45
16 -print("Git directory:", DIR) 46 + self.headers = set(includes_system.findall(inner))
17 47
18 -try:  
19 - TAG = check_output(['git', 'describe', '--tags', '--always'], cwd=str(DIR)).decode("utf-8")  
20 -except CalledProcessError:  
21 - TAG = "A non-git source" 48 + self.body = '\n// From {}\n\n'.format(inc) + inner[inner.find('namespace'):]
22 49
23 -def MakeHeader(out):  
24 - main_header = os.path.join(BDIR, 'CLI', 'CLI.hpp')  
25 - with open(main_header) as f:  
26 - header = f.read() 50 + def __add__(self, other):
  51 + out = copy(self)
  52 + out.headers |= other.headers
  53 + out.body += other.body
  54 + out.verbatim += other.verbatim
  55 + return out
27 56
28 - include_files = includes_local.findall(header) 57 + @property
  58 + def header_str(self):
  59 + return '\n'.join('#include <'+h+'>' for h in sorted(self.headers))
29 60
30 - headers = set()  
31 - output = ''  
32 - for inc in include_files:  
33 - with open(os.path.join(BDIR, inc)) as f:  
34 - inner = f.read()  
35 - headers |= set(includes_system.findall(inner))  
36 - output += '\n// From {inc}\n\n'.format(inc=inc)  
37 - output += inner[inner.find('namespace'):] 61 + @property
  62 + def verbatim_str(self):
  63 + return '\n'.join(self.verbatim)
38 64
39 - header_list = '\n'.join('#include <'+h+'>' for h in headers)  
40 -  
41 - output = '''\ 65 + def __str__(self):
  66 + return '''\
42 #pragma once 67 #pragma once
43 68
44 // Distributed under the 3-Clause BSD License. See accompanying 69 // Distributed under the 3-Clause BSD License. See accompanying
45 // file LICENSE or https://github.com/CLIUtils/CLI11 for details. 70 // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
46 71
47 // This file was generated using MakeSingleHeader.py in CLI11/scripts 72 // This file was generated using MakeSingleHeader.py in CLI11/scripts
48 -// from: {tag} 73 +// from: {self.TAG}
49 // This has the complete CLI library in one file. 74 // This has the complete CLI library in one file.
50 75
51 -{header_list}  
52 -{output}'''.format(header_list=header_list, output=output, tag=TAG) 76 +{self.header_str}
  77 +{self.verbatim_str}
  78 +{self.body}
  79 +'''.format(self=self)
  80 +
53 81
54 - with open(out, 'w') as f:  
55 - f.write(output) 82 +def MakeHeader(output, main_header, include_dir = '../include'):
  83 + # Set tag if possible to class variable
  84 + try:
  85 + HeaderFile.TAG = check_output(['git', 'describe', '--tags', '--always'], cwd=str(DIR)).decode("utf-8")
  86 + except CalledProcessError:
  87 + pass
56 88
57 - print("Created {out}".format(out=out)) 89 + base_dir = os.path.abspath(os.path.join(DIR, include_dir))
  90 + main_header = os.path.join(base_dir, main_header)
  91 +
  92 + with open(main_header) as f:
  93 + header = f.read()
  94 +
  95 + include_files = includes_local.findall(header)
  96 +
  97 + headers = [HeaderFile(base_dir, inc) for inc in include_files]
  98 + single_header = reduce(operator.add, headers)
  99 +
  100 + with open(output, 'w') as f:
  101 + f.write(str(single_header))
  102 +
  103 + print("Created", output)
58 104
59 105
60 if __name__ == '__main__': 106 if __name__ == '__main__':
61 parser = argparse.ArgumentParser() 107 parser = argparse.ArgumentParser()
62 - parser.add_argument("output", nargs='?', default=os.path.join(BDIR, 'CLI11.hpp')) 108 + parser.add_argument("output", help="Single header file output")
  109 + parser.add_argument("--main", default='CLI/CLI.hpp', help="The main include file that defines the other files")
  110 + parser.add_argument("--include", default='../include')
63 args = parser.parse_args() 111 args = parser.parse_args()
64 - MakeHeader(args.output) 112 +
  113 + MakeHeader(args.output, args.main, args.include)
  114 +
tests/OptionalTest.cpp
1 #include <cstdlib> 1 #include <cstdlib>
2 #include <iostream> 2 #include <iostream>
3 3
4 -#ifdef __has_include  
5 -#if defined(CLI11_CPP17) && __has_include(<optional>)  
6 -#include <optional>  
7 -#define have_optional 1  
8 -using std::experimental::optional;  
9 -#elif defined(CPP11_CPP14) && __has_include(<experimental/optional>)  
10 -#include <experimental/optional>  
11 -#define have_optional 1  
12 -using std::optional;  
13 -#else  
14 -#define have_optional 0  
15 -#endif  
16 -#endif  
17 -  
18 -#if have_optional  
19 -  
20 -template <typename T> std::istream &operator>>(std::istream &in, optional<T> &val) {  
21 - T v;  
22 - in >> v;  
23 - val = v;  
24 - return in;  
25 -} 4 +#if CLI11_OPTIONAL
26 5
27 #include "app_helper.hpp" 6 #include "app_helper.hpp"
28 7