mirror of
https://github.com/isocpp/CppCoreGuidelines.git
synced 2024-03-22 13:30:58 +08:00
I.30: Encapsulate rule violations
Fiexed #893 by moving the bad example from ES.28 to a new rule: I.30: Encapsulate rule violations. I may inadvertenly have invented a new suppression syntax
This commit is contained in:
parent
17ccab5836
commit
9620ea8d43
|
@ -1201,6 +1201,7 @@ Interface rule summary:
|
||||||
* [I.24: Avoid adjacent unrelated parameters of the same type](#Ri-unrelated)
|
* [I.24: Avoid adjacent unrelated parameters of the same type](#Ri-unrelated)
|
||||||
* [I.25: Prefer abstract classes as interfaces to class hierarchies](#Ri-abstract)
|
* [I.25: Prefer abstract classes as interfaces to class hierarchies](#Ri-abstract)
|
||||||
* [I.26: If you want a cross-compiler ABI, use a C-style subset](#Ri-abi)
|
* [I.26: If you want a cross-compiler ABI, use a C-style subset](#Ri-abi)
|
||||||
|
* [I.30: Encapsulate rule violations](#Ri-encapsulate)
|
||||||
|
|
||||||
See also
|
See also
|
||||||
|
|
||||||
|
@ -2131,6 +2132,69 @@ If you use a single compiler, you can use full C++ in interfaces. That may requi
|
||||||
|
|
||||||
(Not enforceable) It is difficult to reliably identify where an interface forms part of an ABI.
|
(Not enforceable) It is difficult to reliably identify where an interface forms part of an ABI.
|
||||||
|
|
||||||
|
### <a name="Ri-encapsulate"></a>I.30: Encapsulate rule violations
|
||||||
|
|
||||||
|
##### Reason
|
||||||
|
|
||||||
|
To keep code simple and safe.
|
||||||
|
Sometimes, ugly, unsafe, or error-prone techniques are necessary for logical or performance reasons.
|
||||||
|
If so, keep them local, rather than "infecting" interfaces so that larger groups of programmers have to be aware of the
|
||||||
|
subtleties.
|
||||||
|
Implementation complexity should, if at all possible, not leak through interfaces into user code.
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
Consider a program that, depending on some form of input (e.g., arguments to `main`), should consume input
|
||||||
|
from a file, from the command line, or from standard input.
|
||||||
|
We might write
|
||||||
|
|
||||||
|
bool owned;
|
||||||
|
owner<istream*> inp;
|
||||||
|
switch (source) {
|
||||||
|
case std_in: owned = false; inp = &cin;
|
||||||
|
case command_line: owned = true; inp = new istringstream{argv[2]};
|
||||||
|
case file: owned = true; inp = new ifstream{argv[2]};
|
||||||
|
}
|
||||||
|
istream& in = *inp;
|
||||||
|
|
||||||
|
This violated the ruly [against uninitialized variables](#Res-always),
|
||||||
|
the rule against [ignoring ownership](#Ri-raw),
|
||||||
|
and the rule [against magic constants](#Res-magic) .
|
||||||
|
In particular, someone has to remember to somewhere write
|
||||||
|
|
||||||
|
if (owned) delete inp;
|
||||||
|
|
||||||
|
We could handle this particular example by using `unique_ptr` with a special deleter that does nothing for `cin`,
|
||||||
|
but that's complicated for novices (who can easily encounter this problem) and the example is an example of a more general
|
||||||
|
problem where a property that we would like to consider static (here, ownership) needs infrequesntly be addressed
|
||||||
|
at run time.
|
||||||
|
The common, most frequent, and safest examples can be handled statically, so we don't want to add cost and complexity to those.
|
||||||
|
But we must also cope with the uncommon, less-safe, and necessarily more expensive cases.
|
||||||
|
Such examples are discussed in [[Str15]](http://www.stroustrup.com/resource-model.pdf).
|
||||||
|
|
||||||
|
So, we write a class
|
||||||
|
|
||||||
|
class Istream { [[gsl::suppress(lifetime)]]
|
||||||
|
public:
|
||||||
|
enum Opt { from_line=1 };
|
||||||
|
Istream() { }
|
||||||
|
Istream(zstring p) :owned{true}, inp{new ifstream{p}} {} // read from file
|
||||||
|
Istream(zstring p,Opt) :owned{true}, inp{new istringstream{p}} {} // read from command line
|
||||||
|
~Itream() { if (owned) delete inp; }
|
||||||
|
operator istream& () { return *inp; }
|
||||||
|
private:
|
||||||
|
bool owned = false;
|
||||||
|
istream* inp = &cin;
|
||||||
|
};
|
||||||
|
|
||||||
|
Now, the dynamic nature of `istream` ownership has been encapsulated.
|
||||||
|
Presumably, a bit of checking for potential errors would be added in real code.
|
||||||
|
|
||||||
|
##### Enforcement
|
||||||
|
|
||||||
|
* Hard, it is hard to decide what rule-breaking code is essential
|
||||||
|
* flag rule suppression that enable rule-violations to cross interfaces
|
||||||
|
|
||||||
# <a name="S-functions"></a>F: Functions
|
# <a name="S-functions"></a>F: Functions
|
||||||
|
|
||||||
A function specifies an action or a computation that takes the system from one consistent state to the next. It is the fundamental building block of programs.
|
A function specifies an action or a computation that takes the system from one consistent state to the next. It is the fundamental building block of programs.
|
||||||
|
@ -10245,17 +10309,6 @@ It nicely encapsulates local initialization, including cleaning up scratch varia
|
||||||
|
|
||||||
If at all possible, reduce the conditions to a simple set of alternatives (e.g., an `enum`) and don't mix up selection and initialization.
|
If at all possible, reduce the conditions to a simple set of alternatives (e.g., an `enum`) and don't mix up selection and initialization.
|
||||||
|
|
||||||
##### Example
|
|
||||||
|
|
||||||
bool owned = false;
|
|
||||||
owner<istream*> inp = [&]{
|
|
||||||
switch (source) {
|
|
||||||
case default: owned = false; return &cin;
|
|
||||||
case command_line: owned = true; return new istringstream{argv[2]};
|
|
||||||
case file: owned = true; return new ifstream{argv[2]};
|
|
||||||
}();
|
|
||||||
istream& in = *inp;
|
|
||||||
|
|
||||||
##### Enforcement
|
##### Enforcement
|
||||||
|
|
||||||
Hard. At best a heuristic. Look for an uninitialized variable followed by a loop assigning to it.
|
Hard. At best a heuristic. Look for an uninitialized variable followed by a loop assigning to it.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user