Wywołanie funkcji static_assert w template

0

Cześć.

Mam taki program z funkcją do rzutowanie typów:

#include <iostream>
#include <cstddef>
#include <cmath>
#include <concepts>
#include <cassert>

template <typename T> constexpr auto make_false() { return false; }
template <typename Dst, typename Src>
auto safe_cast(const Src& v) -> Dst {
  using namespace std;
  constexpr auto is_same_type = is_same_v<Src, Dst>;
  constexpr auto is_pointer_to_pointer = is_pointer_v<Src> && is_pointer_v<Dst>;
  constexpr auto is_float_to_float = is_floating_point_v<Src> && is_floating_point_v<Dst>;
  constexpr auto is_number_to_number = is_arithmetic_v<Src> && is_arithmetic_v<Dst>;
  constexpr auto is_intptr_to_ptr = (is_same_v<uintptr_t, Src> || is_same_v<intptr_t,Src>) &&
    is_pointer_v<Dst>;
  constexpr auto is_ptr_to_intptr = is_pointer_v<Src> && 
    (is_same_v<uintptr_t, Dst> || is_same_v<intptr_t,Dst>);
  if constexpr(is_same_type) {
    return v;
  }
  else if constexpr(is_intptr_to_ptr || is_ptr_to_intptr) {
    return reinterpret_cast<Dst>(v);
  }
  else if constexpr(is_pointer_to_pointer) {
    assert(dynamic_cast<Dst>(v) != nullptr);
    return static_cast<Dst>(v);
  }
  else if constexpr (is_float_to_float) {
    auto casted = static_cast<Dst>(v);
    auto casted_back = static_cast<Src>(v);
    assert(!isnan(casted) && !isnan(casted_back));
    return casted;
  }
  else if constexpr( is_number_to_number) {
    auto casted = static_cast<Dst>(v);
    auto casted_back = static_cast<Src>(casted);
    assert(casted == casted_back);
    return casted;
  }
  else {
    static_assert(make_false<Src>(),"CastError");
    return Dst{};
  }
}

int main() {
 auto x = safe_cast<int>("aaaaa");
}

Jak zamienie static_assert(make_false<Src>(),"CastError"); na static_assert(false,"CastError"); to wtedy staic_assert wykona się zawsze dla wszystkich typów nawet jak rzutowanie będzie poprawne. To wynika z tego ze template wcześniej jest sprawdzany przez kompilator? Myślałem, że template zostanie utoworzony dopiero jak wywołana zostanie funkcji safe_cast.

3

Musisz uzależnić ten static_assert w jakiś sposób od template argument, możesz to zrobić na kilka sposobów, najczęściej albo robi się helper variable template albo IILE: https://godbolt.org/z/zMG4416G5 (funkcja, którą zaproponowałes również może zostać użyta, ale zwraca tylko jedną wartość, więc można od razu template variable zrobić)

Chciałem dodać komentarz ale forum mi nie pozwala - tutaj wyjaśnienie: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2593r1.html#the-actual-rule

3

To wynika z tego ze template wcześniej jest sprawdzany przez kompilator?

Błędny trop. W uproszczeniu static_assert(false) jest traktowany tak samo jak błąd składniowy, czyli np. jak brak średnika, czyli nie ma znaczenia w którym miejscu w drabince if constexpr się znajduje.

(edit)
@B4mbus Apropos https://eel.is/c++draft/temp.res#general-6 nie napisałem, że to nie może się zadziać, tylko, że to błędny trop i nie stoi to w kontradykcji na temat tego co napisałem o static_assert(false), które jest faktyczną odpowiedzią na pytanie. Poza tym, myślnik niżej masz jawnie napisane, że zignorowanie static_assert(false) jest UB.

2

@ProgrammingKing Postanowiłem to wynieść do regularnej dyskusji, jako, że rozmawiamy w temacie.

Co to znaczy UB ? Chodzi mi o zdanie "że zignorowanie static_assert(false) jest UB."

UB - undefined behaviour. Draft określa tą sytuację jako ill-formed, no diagnostic required ale to zawiłe określenie to generalnie synonim UB w standardzie C++. W tym konkretnym przypadku oznacza to mniej więcej tyle, że standard nie przewiduje oraz nie definiuje w ogóle takiej możliwości jak zignorowanie static_assert(false);

0

Dziękuje za pomoc.

2

A ja się przyczepię: co ma włąściwie robić safe_cast? Bo jak widzę tam gdzieś reinterpret_cast to nie czuje się bezpieczny.
I jescze jaki standard C++ używasz? Może koncepty z C++20 uroszcżą ten kod.
Zalecam też zainteresować się, pisaniem testów.
Taką uniwersalną funckję jak safe_cast to bym naprawdę mocno obłożył testami (cokolwiek ma to robić).

1

Używam standardu C++ 20. A tą funkcję wziąłem z książki bo akurat ucze się konceptów i tam był taki przykład (akurat bez konceptów). A czym Byś zastąpił reinterpret_cast w tym przypadku ?

generalnie jak widzę w kodzie reintepret_cast to od razu dopatruję się błędów.
W wypadku tej funkcji problem jest brak zrozumienia czym jest intptr_t.
To nie jest jakiś specjalny typ do reprezentowania adresu jako liczby całkowitej, ale jedynie alias na jakiś całkowity typ wbudowany którego rozmiar jest równy rozmiarowi wskaźnika.
Ergo przy czymś takim co ma się nazywać safe_cast i gdy nie ma pewności czy long int reprezentować faktyczną liczbę, czy może trzym adres ten skrótowiec jest zaproszeniem do dziwnych błędów.
Po prostu w moim umyśle safe nie łączy się z reintepret_cast

https://godbolt.org/z/W6e1z8Wq3

IMHO ten safe_cast próbuje robić za dużo i przez to nie jest już safe.
Zresztą jak zboczyłem nazwę safe_cast to przypuszczałem, że to będzie rzucać wyjątkami, jeśli przy konwersji dochodzi do integer overflow.

W tej funkcji jest jeszcze parę innych dziwnych rzeczy, ale nie będę się ich czepiał, ale mam wątpliwości co do tej książki.

1 użytkowników online, w tym zalogowanych: 0, gości: 1