Кресты / Говнокод #24803 Ссылка на оригинал

0

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
struct A { 
  virtual int transmogrify();
};
 
struct B : A {
  int transmogrify() override { new(this) A; return 2; }
};
 
int A::transmogrify() { new(this) B; return 1; }
 
static_assert(sizeof(B) == sizeof(A));

int main() {
  A i;
  int n = i.transmogrify();
  // int m = i.transmogrify(); // undefined behavior
  int m = std::launder(&i)->transmogrify(); // OK
  assert(m + n == 3);
}

Yo dawg, we heard you like kostyli, so we put our kostyli into your kostyli, so that you can use kostyli to support our kostyli!

https://en.cppreference.com/w/cpp/utility/launder

А если серьезно, мне еще не удалось соорудить пример, чтоб код с std::launder и без него работали по разному.

Запостил: Elvenfighter Elvenfighter, (Updated )

Комментарии (19) RSS

  • Чем "std::launder" отличается от "*"?
    Ответить
    • Ну, std::launder не разыменовывает указатель. Он тупо возвращает то, что получил.

      Внутри std::launder находится компиляторо-специфический костыль, который можно назвать "optimization barrier", который сигнализирует компилятору "смотри, жывотное, что под указателем, а не в свои сраные кеши". Где-то читал, что в GCC std::launder был реализован просто:
      template <typename _Tp>
      _Tp * launder(_Tp *__p) { return __p; }


      Но потом хлопцы из LLVM/Clang подсуетились и показали who is who, продемонстрировав пример, когда гнутая реализация делала ересь.
      Ответить
      • X *p = new X{3, 4};
          const int a = p->n;
          X* np = new (p) X{5, 6};    // p does not point to new object because X::n is const; np does
          const int b = p->n; // undefined behavior
          const int c = p->m; // undefined behavior (even though m is non-const, p can't be used)


        Почему тогда здесь убэ, а с launder - не убэ?
        Ответить
        • В первом случае коспилятор мог бы закостылизировать закешировать (на стеке, напр.) значение p->n (ведь X::n -- const int и он не должен менятся, так?). И выражение мог бы p->n заменить на использование кеша. Т.е. вместо ожидаемого 5 в const int b внезапно при выполнении окажется 3.

          Во втором случае комитет вероятно пошел еще дальше, и сказал "А что если компилятор закеширует целую структуру под указателем? Ведь мы итак знаем, что итак X::n у нас const и читать его после замены без std::launder -- UB." И дальше все так же как и в предыдущем пункте.
          Ответить
          • То есть уба не будет, если оба не конст?
            Ответить
  • Нихуя не понятно. Переведи на "PHP".
    Ответить
      • Ввиду некоторой ограниченности языка «PHP» (в частности, невозможности перезаписать $this), точный перевод невозможен. Однако, позволив себе некоторую вольность, этот код можно перевести вот так:
        <?
        function sizeof_($class)
        {
            $before = memory_get_usage();
            $ret = new $class();
            $after = memory_get_usage();
            return ($after - $before);
        }
        
        function launder($p) {
            return $p;
        }
        
        class A {
            function transmogrify_cpp_translation() {
                return 1;
            }
            function transmogrify() {
                return unserialize(preg_replace('/^O:\d+:"[^"]++"/','O:1:"B"', serialize($this)));
            }
        }
        
        class B extends A {
            function transmogrify_cpp_translation() {
                return 2;
            }
            function transmogrify() {
                return unserialize(preg_replace('/^O:\d+:"[^"]++"/','O:1:"A"', serialize($this)));
            }
        }
        
        if (sizeof_("A") != sizeof_("B")) {
            echo "error: static assertion failed";
        }
        
        $i = new A();
        $i = $i->transmogrify();
        $n = $i->transmogrify_cpp_translation();
        // $i = $i->transmogrify(); // undefined behavior
        $i = launder($i)->transmogrify();
        $m = $i->transmogrify_cpp_translation();
        if ($n + $m != 3) {
            echo "assertation \"\$n + \$m != 3\" failed.";
        }
        ?>

        http://sandbox.onlinephpfunctions.com/code/b7e8dd12f4d3914031e4784fd6768b1f452f3c41
        Ответить

Добавить комментарий

Семь раз отмерь — один отрежь, guest!

    А не использовать ли нам bbcode?


    8