I am pretty surprised that this struct, which is only explicitly convertible to bool, works fine inside a if statement:
struct A
{
explicit operator bool( ) const
{
return m_i % 2 == 0;
}
int m_i;
};
int main()
{
A a{ 10 };
if ( a ) // this is considered explicit
{
bool b = a; // this is considered implicit
// and therefore does not compile
}
return 0;
}
Why is it so? What is the design reason behind it in the C++ Standard?
I personally find more explicit the second conversion than the first one. To make it even more clear, I would have expected the compiler forcing to have the following for both the cases:
int main()
{
A a{ 10 };
if ( (bool)a )
{
bool b = (bool)a;
}
return 0;
}
§6.4 Selection statements [stmt.select]
The value of a condition that is an expression is the value of the expression, contextually converted to bool for statements other than switch;
§4 Standard conversions [conv]
Certain language constructs require that an expression be converted to
a Boolean value. An expression e appearing in such a context is said
to be contextually converted to bool and is well-formed if and only
if the declaration bool t(e); is well-formed, for some invented
temporary variable t (8.5).
So the expression of the condition in if must be contextually convertible to bool, which means that explicit conversions are allowed.
This is mode most likely done because the condition of if can only evaluate to a boolean value, so by saying if(cond) you are explicitly stating you want cond to be evaluated to a boolean value.
Related
Let's say I have a struct with state, and a few member functions on that struct.
Let's say that the struct member returns an instance of its own type, and I call additional functions on that instance, and pass the result of calling some other member on the initial instance as an argument.
Is the order of invocation between the first invocation, and the argument invocation, guaranteed?
(This pattern comes up a lot when trying to build "builder" type objects that have some internal state, like an expression stack.)
package main
import (
"fmt"
)
type q struct {
val int
}
func (s *q) getVal() int {
return s.val
}
func (s *q) a() *q {
s.val += 1
return s
}
func (s *q) b(i int) int {
return i + s.val
}
func main() {
s := &q{}
// this currently prints 2
// but is that guaranteed?
fmt.Println(s.a().b(s.getVal()))
}
Specifically, is the relative invocation order of s.a() versus s.getVal() guaranteed?
Golang defines the "lexical left-to-right order," but only for an individual expression, and s.a().b() seems like it's technically a different expression than s.getVal().
The behavior it currently has is the behavior I'd want and expect, but I can't tell whether it's also a behavior I can rely on "forever."
The relevant portion of the spec is:
all function calls, method calls, and communication operations are evaluated in lexical left-to-right order.
and
At package level, initialization dependencies override the left-to-right rule for individual initialization expressions, but not for operands within each expression:
I'm trying to create a function that will produce an if condition from a predefined array.
for example:
package errors
type errorCase struct {
// This is the field I need to get in another struct
Field string
// The comparison operator
TestOperator string
// The value that the expected one should not with equal...
WrongValue interface{}
}
var ErrorCases = []*errorCase{ {
"MinValue",
"<",
0,
}, {
"MaxValue",
"==",
0,
}}
Actually I made a new function with a for loop that iterate through all of these "error cases"
func isDirty(questionInterface models.QuestionInterface) bool {
for _, errorCase := range errors.ErrorCases {
s := reflect.ValueOf(&questionInterface).Elem()
value := s.Elem().FieldByName(errorCase.Field)
// At this point I need to create my if condition
// to compare the value of the value var and the wrong one
// With the given comparison operator
}
// Should return the comparison test value
return true
}
Is that possible to create an if condition like that?
With the reflect package?
I think this is possible but I don't find where I should start.
This is possible. I built a generic comparison library like this once before.
A comparison, in simple terms, contains 3 parts:
A value of some sort, on the left of the comparison.
An operator (=, <, >, ...).
A value of some sort, on the right of the comparison.
Those 3 parts, contain only two different types - value and operator. I attempted to abstract those two types into their base forms.
value could be anything, so we use the empty interface - interface{}.
operator is part of a finite set, each with their own rules.
type Operator int
const (
Equals Operator = 1
)
Evaluating a comparison with an = sign has only one rule to be valid - both values should be of the same type. You can't compare 1 and hello. After that, you just have to make sure the values are the same.
We can implement a new meta-type that wraps the requirement for evaluating an operator.
// Function signature for a "rule" of an operator.
type validFn func(left, right interface{}) bool
// Function signature for evaluating an operator comparison.
type evalFn func(left, right interface{}) bool
type operatorMeta struct {
valid []validFn
eval evalFn
}
Now that we've defined our types, we need to implement the rules and comparison functions for Equals.
func sameTypes(left, right interface{}) bool {
return reflect.TypeOf(left).Kind() == reflect.TypeOf(right).Kind()
}
func equals(left, right interface{}) bool {
return reflect.DeepEqual(left, right)
}
Awesome! So we can now validate that our two values are of the same type, and we can compare them against each other if they are. The last piece of the puzzle, is mapping the operator to its appropriate rules and evaluation and having a function to execute all of this logic.
var args = map[Operator]operatorMeta{
Equals: {
valid: []validFn{sameTypes},
eval: equals,
},
}
func compare(o Operator, left, right interface{}) (bool, error) {
opArgs, ok := args[o]
if !ok {
// You haven't implemented logic for this operator.
}
for _, validFn := range opArgs.valid {
if !validFn(left, right) {
// One of the rules were not satisfied.
}
}
return opArgs.eval(left, right), nil
}
Let's summarize what we have so far:
Abstracted a basic comparison into a value and operator.
Created a way to validate whether a pair of values are valid for an operator.
Created a way to evaluate an operator, given two values.
(Go Playground)
I hope that I gave some insight into how you can approach this. It's a simple idea, but can take some boilerplate to get working properly.
Good luck!
I have a question about types of constants which are restricted to certain values and how you accomplish that in Go. Say I create a type unary which has two constant values Positive(1) and Negative(-1) and I want to restrict the user of that type (unary) from creating other values of type unary. Do I achieve this by creating a package and making the values Positive and Negative visible and making the type unary restricted to the containing package? See code below for example
package unary
type unary int////not visible outside of the package unary
const (
Positive unary = 1//visible outside of the package unary
Negative unary = -1//visible outside of the package unary
)
func (u unary) String() string {//visible outside of the package unary
if u == Positive {
return "+"
}
return "-"
}
func (u unary) CalExpr() int {//visible outside of the package unary
if u == Positive {
return 1
}
return -1
}
Is this the correct way to restrict a type to certain constant values?
Flaws
Your proposed solution is not safe in a way you want it to be. One can use untyped integer constants to create new values of unary having a different int value than 1 or -1. See this example:
p := unary.Positive
fmt.Printf("%v %d\n", p, p)
p = 3
fmt.Printf("%v %d\n", p, p)
Output will be:
+ 1
- 3
We could change p's value to store the int value 3 which is obviously not equal to Positive nor to Negative. This is possible because Spec: Assignability:
A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:
...
x is an untyped constant representable by a value of type T.
3 is an untyped constant, and it is representable by a value of type unary which has underlying type int.
In Go you can't have "safe" constants of which "outsider" packages cannot create new values of, for the above mentioned reason. Because if you want to declare constants in your package, you can only use expressions that have "untyped" versions–which may be used by other packages too in assignments (just as in our example).
Unexported struct
If you want to fulfill the "safe" part, you may use unexported structs, but then they cannot be used in constant declarations.
Example:
type unary struct {
val int
}
var (
Positive = unary{1}
Negative = unary{-1}
)
func (u unary) String() string {
if u == Positive {
return "+"
}
return "-"
}
func (u unary) CalExpr() int {
return u.val
}
Attempting to change its value:
p := unary.Positive
p.val = 3 // Error: p.val undefined (cannot refer to unexported field or method val)
p = unary.unary{3} // Error: cannot refer to unexported name unary.unary
// Also error: implicit assignment of unexported field 'val' in unary.unary literal
Note that since we're now using a struct, we can further simplify our code by adding the string representation of our values to the struct:
type unary struct {
val int
str string
}
var (
Positive = unary{1, "+"}
Negative = unary{-1, "-"}
)
func (u unary) String() string { return u.str }
func (u unary) CalExpr() int { return u.val }
Note that this solution still has a "flaw": it uses exported global variables (more precisely package-level variables) whose values can be changed by other packages. It's true that other packages cannot create and assign new values, but they can do so with existing values, e.g.:
unary.Positive = unary.Negative
If you want to protect yourself from such misuse, you also have to make such global variables unexported. And then of course you have to create exported functions to expose those values, for example:
var (
positive = unary{1}
negative = unary{-1}
)
func Positive() unary { return positive }
func Negative() unary { return negative }
Then acquiring/using the values:
p := unary.Positive()
Interface
Care must be taken if you plan to use an interface type for your "constants". An example can be seen in Kaveh Shahbazian's answer. An unexported method is used to prevent others from implementing the interface, giving you the illusion that others truly can't implement it:
type Unary interface {
fmt.Stringer
CalExpr() int
disabler() // implementing this interface outside this package is disabled
}
var (
Positive Unary = unary(1) // visible outside of the package unary
Negative Unary = unary(-1) // visible outside of the package unary
)
type unary int // not visible outside of the package unary
func (u unary) disabler() {}
func (u unary) String() string { /* ... */ }
func (u unary) CalExpr() int { /* ... */ }
This is not the case however. With a dirty trick, this can be circumvented. The exported Unary type can be embedded, and an existing value can be used in order to implement the interface (along with the unexported method), and we can add our own implementations of the exported methods, doing / returning whatever we want to.
Here is how it may look like:
type MyUn struct {
unary.Unary
}
func (m MyUn) String() string { return "/" }
func (m MyUn) CalExpr() int { return 3 }
Testing it:
p := unary.Positive
fmt.Printf("%v %d\n", p, p)
p = MyUn{p}
fmt.Printf("%v %d\n", p, p.CalExpr())
Output:
+ 1
/ 3
Special case
As Volker mentioned in his comment, in your special case you could just use
type unary bool
const (
Positive unary = true
Negative unary = false
)
As the type bool has two possible values: true and false, and we've used all. So there are no other values that could be "exploited" to create other values of our constant type.
But know that this can only be used if the number of constants is equal to the number of possible values of the type, so the usability of this technique is very limited.
Also keep in mind that this does not prevent such misuses when a type of unary is expected, and someone accidentally passes an untyped constant like true or false.
If you like to just work with int without introducing a wrapper type: a classic way to do that in Go is using a public interface with a private function; so everybody can use it but nobody can implement it; like:
type Unary interface {
fmt.Stringer
CalExpr() int
disabler() //implementing this interface outside this package is disabled
}
var (
Positive Unary = unary(1) //visible outside of the package unary
Negative Unary = unary(-1) //visible outside of the package unary
)
type unary int //not visible outside of the package unary
func (u unary) disabler() {}
func (u unary) String() string { //visible outside of the package unary
if u == Positive {
return "+"
}
return "-"
}
func (u unary) CalExpr() int { //visible outside of the package unary
if u == Positive {
return 1
}
return -1
}
Others are able to set Positive to nil though; but that's not a thing in Go world - in such cases.
As #icza mentioned, one can overwrite public methods. But for private methods, Go will not call "the most shallow" one, but instead calls the original.
I use C++11 lambdas quite a lot, and I've often run into compile errors on multiline lambdas because I forgot to add the return type, as is expected, but I recently ran into one example that doesn't have this issue. It looks something like this:
auto testLambda = [](bool arg1, bool arg2)
{
if (arg1)
{
if (!arg2)
{
return false;
}
return true;
}
return false;
};
This compiles just fine even though there's no return type specified. Is this just Visual Studio being dumb and allowing something it shouldn't, or can lambdas just always deduce intrinsic types?
I tried this with return values of all ints or floating point values and it also compiled just fine. I just found this to be really surprising so I wanted to be absolutely sure how it works before I start making assumptions and omitting return types that might break later on.
Lambdas follow the same template deduction rules as auto-returning functions:
Template argument deduction is used in declarations of functions, when deducing the meaning of the auto specifier in the function's return type, from the return statement.
For auto-returning functions, the parameter P is obtained as follows: in T, the declared return type of the function that includes auto, every occurrence of auto is replaced with an imaginary type template parameter U. The argument A is the expression of the return statement, and if the return statement has no operand, A is void(). After deduction of U from P and A following the rules described above, the deduced U is substituted into T to get the actual return type:
auto f() { return 42; } // P = auto, A = 42:
// deduced U = int, the return type of f is int
If such function has multiple return statements, the deduction is performed for each return statement. All the resulting types must be the same and become the actual return type.
If such function has no return statement, A is void() when deducing.
Note: the meaning of decltype(auto) placeholder in variable and function declarations does not use template argument deduction.
Go's interface{} type is both the best and most annoying feature of the language, I find. I'm trying to create a simple user-customisable validation rule solution where the user can define:
The comparison operator.
The comparison operand.
The map key that leads to the value to test.
As well as a simple Boolean expression parser that allows the user to combine multiple rules using AND and OR. So far it all works well, the expressions can be parsed, tokenised, and evaluated successfully, but it's running the rules on the given data that causes problems.
This is the current version of the function that actually evaluates the data:
/*
validate returns a boolean value denoting whether a test was successful. This
function will panic if the type assertions fail.
*/
func (sfvre standardFieldValidationRuleEntry) validate(fieldValue interface{}) bool {
switch sfvre.Operator() {
case VROP_EQUAL:
return fieldValue == sfvre.ComparisonOperand()
case VROP_NEQUAL:
return fieldValue != sfvre.ComparisonOperand()
case VROP_GT:
return fieldValue.(int) > sfvre.ComparisonOperand().(int)
case VROP_LT:
return fieldValue.(int) < sfvre.ComparisonOperand().(int)
case VROP_GTET:
return fieldValue.(int) >= sfvre.ComparisonOperand().(int)
case VROP_LTET:
return fieldValue.(int) <= sfvre.ComparisonOperand().(int)
case VROP_CONTAINS:
return strings.Contains(fieldValue.(string), sfvre.ComparisonOperand().(string))
case VROP_NCONTAINS:
return !strings.Contains(fieldValue.(string), sfvre.ComparisonOperand().(string))
default:
return false
}
}
At the moment the operator implies whether the data is numeric (greater than, less than, etc.). The type assertion to int did the job while building the other parts of the package, but the finished system should also be able to take float64 and be able to handle mixed type comparisons.
The only way I can see of doing this at the moment is by having multiple nested type switches, a level for each of:
The operator.
The type of the field value given.
The type of the comparison operand.
But this has the potential to become very large and not easily manageable. Is there a 'cleaner' way to do this that I can't see, or am I stuck using nested switches?
The solution I've got as of now (thanks to #Volker for the suggestion) does a quick type switch on the values that need comparing and then instead of using the originals in the Operator() switch, it uses the concrete float values:
/*
validate returns a boolean value denoting whether a test was successful. This
function will panic if the type assertions fail.
*/
func (sfvre standardFieldValidationRuleEntry) validate(fieldValue interface{}) bool {
var floatFieldVal, floatCompVal float64
//If the interface is int or float, convert it to a statically typed float64.
switch fieldValue.(type) {
case int:
floatFieldVal = float64(fieldValue.(int))
case float64:
floatFieldVal = fieldValue.(float64)
}
//Do the same with the comparison value.
switch sfvre.ComparisonOperand().(type) {
case int:
floatCompVal = float64(sfvre.ComparisonOperand().(int))
case float64:
floatCompVal = sfvre.ComparisonOperand().(float64)
}
switch sfvre.Operator() {
case VROP_EQUAL:
return fieldValue == sfvre.ComparisonOperand()
case VROP_NEQUAL:
return fieldValue != sfvre.ComparisonOperand()
case VROP_GT:
return floatFieldVal > floatCompVal
case VROP_LT:
return floatFieldVal < floatCompVal
case VROP_GTET:
return floatFieldVal >= floatCompVal
case VROP_LTET:
return floatFieldVal <= floatCompVal
case VROP_CONTAINS:
return strings.Contains(fieldValue.(string), sfvre.ComparisonOperand().(string))
case VROP_NCONTAINS:
return !strings.Contains(fieldValue.(string), sfvre.ComparisonOperand().(string))
default:
return false
}
}
It doesn't catch everything, but restricting what operators the user can choose based on what field they're comparing can mitigate this, but that's part of the larger solution so irrelevant here.