Skip to content

Commit 5497947

Browse files
committed
Add strict and optional as casts
1 parent bc02873 commit 5497947

File tree

3 files changed

+58
-1
lines changed

3 files changed

+58
-1
lines changed

include/cpp2util.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,27 @@ auto as( X const* x ) -> C const* {
626626
return dynamic_cast<C const*>(x);
627627
}
628628

629+
template <typename C, typename X>
630+
inline constexpr auto program_violates_strict_type_safety_guarantee =
631+
std::is_same_v<C, X>
632+
|| requires (X x){ C{x}; }
633+
|| std::is_base_of_v<C, X>
634+
;
635+
636+
template< typename C, typename T >
637+
auto strict_as(T&& t) -> auto {
638+
static_assert(program_violates_strict_type_safety_guarantee<C, std::remove_cvref_t<T>>, "type cannot be safely cast - consider using optional cast '?' or throwing cast");
639+
return cpp2::as<C>(std::forward<T>(t));
640+
}
641+
642+
template< typename C, typename T >
643+
auto optional_as(T&& t) -> auto {
644+
try {
645+
return std::optional<C>{cpp2::as<C>(std::forward<T>(t))};
646+
} catch(const cpp2::narrowing_error&) {
647+
return std::optional<C>{};
648+
}
649+
}
629650

630651
//-------------------------------------------------------------------------------------------------------------
631652
// std::variant is and as

source/cppfront.cpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1906,7 +1906,33 @@ class cppfront
19061906
return;
19071907
}
19081908

1909-
printer.print_cpp2("cpp2::as<", n.position());
1909+
bool add_strict_as = false;
1910+
bool add_optional_as = false;
1911+
if (auto* postfix = n.terms.front().expr->get_postfix_expression_node()){
1912+
if(auto* id_expr = std::get_if<primary_expression_node::id_expression>(&postfix->expr->expr)) {
1913+
if (auto* unqual = std::get_if<id_expression_node::unqualified>(&(*id_expr)->id)) {
1914+
if ((*unqual)->strict) {
1915+
add_strict_as = true;
1916+
} else if ((*unqual)->optional) {
1917+
add_optional_as = true;
1918+
}
1919+
} else if (auto* qual = std::get_if<id_expression_node::qualified>(&(*id_expr)->id)){
1920+
if ((*qual)->ids.back().id->strict) {
1921+
add_strict_as = true;
1922+
} else if ((*qual)->ids.back().id->optional) {
1923+
add_optional_as = true;
1924+
}
1925+
}
1926+
}
1927+
}
1928+
1929+
if (add_strict_as) {
1930+
printer.print_cpp2("cpp2::strict_as<", n.position());
1931+
} else if (add_optional_as) {
1932+
printer.print_cpp2("cpp2::optional_as<", n.position());
1933+
} else {
1934+
printer.print_cpp2("cpp2::as<", n.position());
1935+
}
19101936
emit(*n.terms.front().expr);
19111937
printer.print_cpp2(">(", n.position());
19121938

source/parse.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,8 @@ struct unqualified_id_node
379379
> arg;
380380
};
381381
std::vector<term> template_args;
382+
token const* strict = {}; // optional
383+
token const* optional = {}; // optional
382384

383385
auto get_token() -> token const* {
384386
if (template_args.empty()) {
@@ -1836,6 +1838,14 @@ class parser
18361838
next();
18371839
}
18381840

1841+
if (curr().type() == lexeme::Not) {
1842+
n->strict = &curr();
1843+
next();
1844+
} else if (curr().type() == lexeme::QuestionMark) {
1845+
n->optional = &curr();
1846+
next();
1847+
}
1848+
18391849
return n;
18401850
}
18411851

0 commit comments

Comments
 (0)