Repository navigation
Expand file tree
/
Copy pathapp.cpp
More file actions
195 lines (178 loc) · 6.48 KB
/
Copy pathapp.cpp
File metadata and controls
195 lines (178 loc) · 6.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#include "app.hpp"
#include <charconv>
#include <optional>
#include <string>
#include <string_view>
#include <system_error>
namespace bitops
{
namespace
{
// Trims ASCII spaces and tabs from both ends. Only spaces/tabs, not every
// whitespace class: input lines here come from a terminal or a pipe, and
// trimming '\n' is already handled by getline.
std::string_view trim(std::string_view text)
{
const std::string_view whitespace = " \t";
const std::size_t first = text.find_first_not_of(whitespace);
if (first == std::string_view::npos)
{
return {};
}
const std::size_t last = text.find_last_not_of(whitespace);
return text.substr(first, last - first + 1);
}
// Reads one integer in [minValue, maxValue], re-prompting on bad input.
// Returns std::nullopt on end-of-input (Ctrl-D / closed stdin) instead of
// looping forever, which the original validator did.
//
// Parsing is deliberately strict, and that strictness is the point: the
// previous version read with `operator>>` and then discarded the rest of
// the line, so "1abc" was silently accepted as menu choice 1 and the next
// prompt consumed whatever the user meant as a separate answer. Reading a
// whole line and requiring `std::from_chars` to consume all of it means
// trailing junk is an error, not a hidden second input. A leading '+' is
// stripped first because from_chars does not accept it, and users do type it.
std::optional<int> readInt(std::istream& in, std::ostream& out, const std::string& prompt,
int minValue, int maxValue)
{
while (true)
{
out << prompt << std::flush;
std::string line;
if (!std::getline(in, line))
{
return std::nullopt;
}
std::string_view text = trim(line);
if (text.empty())
{
out << "\tError: empty input. Please enter a number.\n";
continue;
}
if (text.front() == '+')
{
text.remove_prefix(1);
}
int value = 0;
const char* begin = text.data();
const char* end = text.data() + text.size();
const std::from_chars_result result = std::from_chars(begin, end, value);
if (result.ec == std::errc::result_out_of_range)
{
out << "\tError: number out of range. Please enter a number.\n";
continue;
}
if (result.ec != std::errc() || result.ptr != end)
{
out << "\tError: invalid input. Please enter a whole number with nothing after it.\n";
continue;
}
if (value < minValue || value > maxValue)
{
out << "\tError: value must be between " << minValue << " and " << maxValue << ".\n";
continue;
}
return value;
}
}
std::optional<int> promptBitIndex(std::istream& in, std::ostream& out)
{
return readInt(in, out, "\tChoose a bit index (0-31): ", kMinBitIndex, kMaxBitIndex);
}
void printMenu(std::ostream& out, std::uint32_t currentValue)
{
out << "------------------------Bit Manipulation------------------------\n";
out << "\tCurrent bitField = " << currentValue << "\n";
out << "\tBits: " << toBitString(currentValue) << "\n\n";
out << "\t1) Turn on a bit\n";
out << "\t2) Turn off a bit\n";
out << "\t3) Toggle a bit\n";
out << "\t4) Negate bitField\n";
out << "\t5) Left shift bitField\n";
out << "\t6) Right shift bitField\n";
out << "\t7) Exit\n";
}
} // namespace
void printUsage(std::ostream& out)
{
out << "Usage: bit_manipulation [--help]\n\n";
out << "Interactive demo for bit manipulation on a 32-bit value.\n";
out << "Pick an operation from the menu, watch the value and its binary\n";
out << "representation change, and exit with option 7 or Ctrl-D.\n";
out << "The session starts from " << kInitialValue << ".\n";
}
int runApp(std::istream& in, std::ostream& out, std::uint32_t initialValue)
{
std::uint32_t bitField = initialValue;
bool running = true;
while (running)
{
printMenu(out, bitField);
std::optional<int> choice =
readInt(in, out, "\n\tEnter the number of the operation you'd like to perform: ", 1, 7);
if (!choice.has_value())
{
break;
}
out << "\n";
// The three bit operations differ only in the function applied and
// the past-tense verb, so they share one lambda instead of three
// near-identical switch cases. If the menu grows past ~8 operations,
// this switch should become a data table (id, label, needs-bit flag,
// function) that renders the menu and dispatches from one source of
// truth; at seven operations a table would be more machinery than
// the duplication it removes.
auto runBitOperation = [&](std::uint32_t (*operation)(std::uint32_t, int), const char* verb) {
std::optional<int> bit = promptBitIndex(in, out);
if (!bit.has_value())
{
running = false;
return;
}
bitField = operation(bitField, *bit);
out << "\t" << verb << " bit " << *bit << "!\n";
};
switch (*choice)
{
case 1:
runBitOperation(turnOn, "Turned on");
break;
case 2:
runBitOperation(turnOff, "Turned off");
break;
case 3:
runBitOperation(toggle, "Toggled");
break;
case 4:
bitField = negateBits(bitField);
out << "\tNegated bitField!\n";
break;
case 5:
bitField = leftShift(bitField);
out << "\tLeft shifted bitField!\n";
break;
case 6:
bitField = rightShift(bitField);
out << "\tRight shifted bitField!\n";
break;
case 7:
running = false;
continue;
default:
// Unreachable: readInt already restricts the range, but
// keep the guard so a future change cannot silently fall through.
out << "\tInvalid choice. Please choose a valid option.\n";
continue;
}
if (!running)
{
break;
}
out << "\n\tbitField == " << bitField << "\n";
out << "\tBits:\t" << toBitString(bitField) << "\n\n";
}
out << "Exiting BitOps.\n";
return 0;
}
} // namespace bitops