Skip to content

Commit 6fd8653

Browse files
committed
Add try_dynamic_cast from rvalue to optional
This commit adds templates for `expr_try_dynamic_cast` and `type_try_dynamic_cast` where the parameter is an rvalue and the return type is an `optionalt`. This is implemented by moving the parameter into the `optionalt`. Included are unit tests of the new templates, which show that they return the types and values expected. As well as tests and a static assert for the existing overloads which show that they still return a pointer. These new templates are useful in the case where we are using the result of a function, which returns by value but only in the case where the value can be cast to a given type. For example with the new overloads the following code can be written - ``` exprt enig(); void handle_struct_case(const struct_exprt &struct_expr); void myFunction() { if(const auto my_struct = expr_try_dynamic_cast<struct_exprt>(enig())) handle_struct_case(*my_struct); } ``` However without the new templates and because the old ones do not bind to rvalues, an additional temporary variable otherwise would have to be declared - ``` void myFunction2() { const exprt enigma = enig(); if(const auto my_struct = expr_try_dynamic_cast<struct_exprt>(enigma)) handle_struct_case(*my_struct); } ```
1 parent 25ba4e6 commit 6fd8653

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-0
lines changed

src/util/expr_cast.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,32 @@ auto expr_try_dynamic_cast(TExpr &base)
107107
return ret;
108108
}
109109

110+
/// \brief Try to cast a generic exprt to a specific derived class.
111+
/// \tparam T: The type to cast the `base` param to.
112+
/// \tparam TType: The original type to cast from, Must be a exprt rvalue.
113+
/// \param base: A generic \ref exprt rvalue.
114+
/// \return Cast value in an optionalt<T> or empty if \a base is not an instance
115+
/// of T.
116+
template <typename T, typename TExpr>
117+
optionalt<T> expr_try_dynamic_cast(TExpr &&base)
118+
{
119+
static_assert(
120+
std::is_rvalue_reference<decltype(base)>::value,
121+
"This template overload must only match where base is an rvalue.");
122+
static_assert(
123+
std::is_base_of<exprt, typename std::decay<TExpr>::type>::value,
124+
"Tried to expr_try_dynamic_cast from something that wasn't an exprt.");
125+
static_assert(
126+
std::is_base_of<exprt, T>::value,
127+
"The template argument T must be derived from exprt.");
128+
static_assert(!std::is_const<TExpr>::value, "Attempted to move from const.");
129+
if(!can_cast_expr<T>(base))
130+
return {};
131+
optionalt<T> ret{static_cast<T &&>(base)};
132+
validate_expr(*ret);
133+
return ret;
134+
}
135+
110136
/// \brief Try to cast a reference to a generic typet to a specific derived
111137
/// class
112138
/// \tparam T: The reference or const reference type to \a TUnderlying to cast
@@ -134,6 +160,31 @@ auto type_try_dynamic_cast(TType &base) ->
134160
return ret;
135161
}
136162

163+
/// \brief Try to cast a generic typet to a specific derived class.
164+
/// \tparam T: The type to cast the `base` param to.
165+
/// \tparam TType: The original type to cast from, Must be a typet rvalue.
166+
/// \param base: A generic \ref typet rvalue.
167+
/// \return Cast value in an optionalt<T> or empty if \a base is not an instance
168+
/// of T.
169+
template <typename T, typename TType>
170+
optionalt<T> type_try_dynamic_cast(TType &&base)
171+
{
172+
static_assert(std::is_rvalue_reference<decltype(base)>::value,
173+
"This template overload must only match where base is an rvalue.");
174+
static_assert(
175+
std::is_base_of<typet, typename std::decay<TType>::type>::value,
176+
"Tried to type_try_dynamic_cast from something that wasn't an typet.");
177+
static_assert(
178+
std::is_base_of<typet, T>::value,
179+
"The template argument T must be derived from typet.");
180+
static_assert(!std::is_const<TType>::value, "Attempted to move from const.");
181+
if(!can_cast_type<T>(base))
182+
return {};
183+
optionalt<T> ret{static_cast<T &&>(base)};
184+
validate_type(*ret);
185+
return ret;
186+
}
187+
137188
namespace detail // NOLINT
138189
{
139190

unit/util/expr_cast/expr_cast.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ SCENARIO("expr_dynamic_cast",
2525
{
2626
const exprt &expr=symbol_expr;
2727

28+
// Check pointer overload is used, when casting from reference.
29+
static_assert(
30+
std::is_same<
31+
decltype(expr_try_dynamic_cast<symbol_exprt>(expr)),
32+
const symbol_exprt *>::value,
33+
"expr_try_dynamic_cast on a reference parameter must return a pointer");
34+
2835
THEN("Try-casting from exprt reference to symbol_exprt pointer "
2936
"returns a value")
3037
{
@@ -97,4 +104,111 @@ SCENARIO("expr_dynamic_cast",
97104
REQUIRE_NOTHROW(expr_dynamic_cast<symbol_exprt>(expr_ref));
98105
}
99106
}
107+
GIVEN("An exprt value upcast from a symbolt")
108+
{
109+
exprt expr = symbol_exprt{};
110+
111+
THEN("Trying casting a value to a symbol_exprt should yield a non null "
112+
"pointer.")
113+
{
114+
symbol_exprt *result = expr_try_dynamic_cast<symbol_exprt>(expr);
115+
REQUIRE(result != nullptr);
116+
}
117+
118+
THEN("Trying casting a value to a transt should yield a null pointer.")
119+
{
120+
transt *result = expr_try_dynamic_cast<transt>(expr);
121+
REQUIRE(result == nullptr);
122+
}
123+
124+
THEN("Trying casting an rvalue to a symbol_exprt should yield a non empty "
125+
"optional.")
126+
{
127+
optionalt<symbol_exprt> result =
128+
expr_try_dynamic_cast<symbol_exprt>(std::move(expr));
129+
REQUIRE(result.has_value());
130+
}
131+
132+
THEN("Trying casting an rvalue to a transt should yield a empty optional.")
133+
{
134+
optionalt<transt> result =
135+
expr_try_dynamic_cast<transt>(std::move(expr));
136+
REQUIRE_FALSE(result.has_value());
137+
}
138+
}
139+
}
140+
141+
SCENARIO("type_dynamic_cast",
142+
"[core][utils][expr_cast][type_dynamic_cast]")
143+
{
144+
string_typet string_type;
145+
GIVEN("A typet value upcast from a string_typet")
146+
{
147+
typet type = string_type;
148+
149+
THEN("Trying casting a value to a string_typet should yield a non null "
150+
"pointer.")
151+
{
152+
string_typet *result = type_try_dynamic_cast<string_typet>(type);
153+
REQUIRE(result != nullptr);
154+
}
155+
156+
THEN("Trying casting a value to a struct_typet should yield a null pointer.")
157+
{
158+
struct_typet *result = type_try_dynamic_cast<struct_typet>(type);
159+
REQUIRE(result == nullptr);
160+
}
161+
162+
THEN("Trying casting an rvalue to a symbol_exprt should yield a non empty "
163+
"optional.")
164+
{
165+
optionalt<string_typet> result =
166+
type_try_dynamic_cast<string_typet>(std::move(type));
167+
REQUIRE(result.has_value());
168+
}
169+
170+
THEN("Trying casting an rvalue to a struct_typet should yield a empty "
171+
"optional.")
172+
{
173+
optionalt<struct_typet> result =
174+
type_try_dynamic_cast<struct_typet>(std::move(type));
175+
REQUIRE_FALSE(result.has_value());
176+
}
177+
}
178+
GIVEN("A const typet reference upcast from a string_typet")
179+
{
180+
const typet &type = string_type;
181+
182+
THEN("Trying casting a const reference to a string_typet should yield a "
183+
"non null pointer to const.")
184+
{
185+
const string_typet *result = type_try_dynamic_cast<string_typet>(type);
186+
REQUIRE(result != nullptr);
187+
}
188+
189+
THEN("Trying casting a const reference to a struct_typet should yield a "
190+
"null pointer to const.")
191+
{
192+
const struct_typet *result = type_try_dynamic_cast<struct_typet>(type);
193+
REQUIRE(result == nullptr);
194+
}
195+
}
196+
GIVEN("A typet reference upcast from a string_typet")
197+
{
198+
typet &type = string_type;
199+
200+
THEN("Trying casting a reference to a string_typet should yield a non null "
201+
"pointer.")
202+
{
203+
string_typet *result = type_try_dynamic_cast<string_typet>(type);
204+
REQUIRE(result != nullptr);
205+
}
206+
207+
THEN("Trying casting a reference to a struct_typet should yield a null "
208+
"pointer.")
209+
{
210+
struct_typet *result = type_try_dynamic_cast<struct_typet>(type);
211+
REQUIRE(result == nullptr);
212+
}
213+
}
100214
}

0 commit comments

Comments
 (0)