Ошибки компилятора PascalABC.Net

В 3.5.1.2277 программа выдаёт исключение System.NullReferenceException:

На последней (2326) всё ещё воспроизводится.

Порой гнаться за обновлениями вредно… Выше пример. На мой взгляд, хорошо бы иметь две версии PascalABC.NET - одна обкатанная, другая - самая свежая, но для тестирования.

Это не обоснование. Вы даже не проверили воспроизводится ли у вас на 2326, может это из за .Net или чего то ещё.

Мне достаточно того, что есть человек, у которого данная ошибка воспроизводится. Выяснять почему и что не работает - задача разработчиков, а помогать им в этом - тестировщиков. Я не являюсь ни одним из них. А обновляться просто так, чтобы проверить есть ли ошибка или нет, когда в моей версии поведение адекватное - мне нет желания. Меня и 3.5.1.2277 более чем устраивает.

Можно увидеть пример инстанцирования для:

type
  A<T> = class where T: A<T>;
  end;

? В C# - аналогичный код компилируется, но инстанцировать тип напрямую невозможно, как и здесь, впрочем, что правильно.

Кроме того, Вы утверждайте, что эта конструкция полезна, но я не вижу инстанцирования типа в Вашем же примере:

type
  A<T> = class where T: A<T>;
  end;
  
  B = class(A<B>)
  end;
  
begin
  var x := new B;
  Writeln(x is A);
end.

Именно в паскале не работает из за другой issue, но код правильный и в C# так сработает.

Как я и сказал - весь смысл такого паттерна в том, чтоб избежать дубли кода в наследниках. То есть весь этот такой класс нужен только для наследников.

Я не добавил abstract в минимальный код, потому что это минимальный код. Но в примере (из #2190) он есть.

Ну и самое сладкое, зачем я это вообще придумал:

type
  A<T> = class where T: A<T>;
    static property p1[s: string]: T read default(T); default;
  end;
  
  B = class(A<B>)
  end;
  
begin
  var x := B['abc']; // у "x" тип будет B
end.

(ссылка на более реальный пример уже 2 раза выложена)

using System;

public class A<T> where T : A<T>
{
}

public class B : A<B>
{
}

public class Program
{
    public static void Main()
    {
        var x = new B();
        Console.WriteLine(x is A<B>);
    }
}

У Вас пропущен тип-параметр для A<>, так что нет, он неправильный.

type
  A<T> = class
  public
    static property P[s: string]: T read default(T); default;
  end;
  
  B = class(A<B>)
  end;
  
begin
  var x := B['abc'];
end.

У x также тип - B, так что я не вижу смысла усложнять код, если можно без where.

Вы можете словами объяснить, когда такое реально будет нужно? Не на абстрактном примере, не на каком-то там “фиксере” (который непонятно чем занимается), а на реальном примере из реальной жизни?

Я более чем уверен, что, если данный паттерн полезен, каким Вы его представляйте, то использовался и используется сейчас в проектах. Если найдёте как он называется - можете сюда дать ссылку, пусть люди почитают.

Ну а это не только не_безопасно. В примере который я уже привёл - используются поля типа Fixer на этом же T.

Как я и сказал, то как красиво наследуется статичное дефолтное свойство - это то что особо приятно. Но пример приведён полный.

Это не абстрактный пример, это то что мне понадобилось в своём коде. И я с радостью дам вам стенку текста, объясняющую всё. Очень хороший способ лишний раз самому всё перепродумать:


1. Группы в .xml

Для начала посмотрите сюда. В этом файле (и ещё 2 других .xml-ках рядом) хронос описывает весь интерфейс всех версий и расширений OpenGL.

Там так же есть так-называемые группы. Это аналог enum-ов из высокоуровневых языков.

Но программный интерфейс для OpenGL который предоставляет хронос - это .h файлы, то есть для языков C/C++. А там группировать константы не принято, вместо это в каждый GLenum параметр можно передать любую константу.

Группы в .xml файлах сделаны только для тех, кто занимается враперами в высокоуровневых языках. И поэтому хронос сам не сильно ими занимается, так что у них есть некоторые проблемы.
issue, конечно, уже сделал, но делать исправления в своём форке не хочу, потому что тогда обновление под-репозитория в POCGL не будет так же просто как вызов git.exe с одной командой.

Решение которое я выбрал - проверенное мною и временем множество раз - сделать отдельный файл, в котором будет что то типа:

#GroupName1
!rename=OtherName

#GroupName2
!addenum
ename=0x12AB

Ну и разумеется этому файлу нужен парсер и экземпляры какого то класса, который может хранить информацию об изменении в самом простом, в том числе в применении, виде.

Формат этот, кстати, я очень давно придумал, ещё во времена SAC (в итоге даже в синтаксис самого SAC попало). Весь смысл как раз в том, что его парсить предельно легко и быстро, нужны только чтение по 1 строке + базовые операции над string. В то же время писать это - полегче чем даже .xml (хоть с безопасностью всё не так же хорошо).


2. Функции

Есть C++ функция:

void p1(int * x)

Что такое x? var-параметр? массив? Любая другая область памяти, из которой можно создать что то типа буфера в OpenGL/OpenCL (то есть тупо указатель)?

Это автоматически не решить. Надо продумывать для каждой функции отдельно. И хардкодить такое будет больно, поэтому, опять же, тот же формат.

# p1
T%2
 -array of integer |

(второй уровень изменений перегрузки, из возможных типов первого параметра убираем массив, оставляя указатель и var-параметр. “|” вообще разделяет параметры, но тут он только для виду)

Это уже реализовано. Но я всё равно хочу немного переделать, сделать чтоб всё смотрелось более одинаково.


3. Описания

(и другие строки которые лучше вынести, как тексты исключений)

Пока используется только в OpenCLABC, но так же планируется для OpenGLABC и может ещё чего-нибудь.

# __NativUtils
# __EventList
%hidden%

В другом файле:

# hidden
--

Применение, конечно, немного другое, но в итоге функционал нужен всё тот же.


4. Другое

Возвращаясь к п.1. - группы это не единственное что может быть не правильно. Я ещё не дошёл до проверки этой части .xml, но в .txt файлах описывающих расширения было много неравномерностей и в целом кривоты.

И кто знает, для чего ещё может понадобится фиксер. И не только в районе групп, мне ещё предстоит для OpenCL подобное делать, а там таких же удобных .xml-ок нету, то есть придётся, наверное, снова парсить .pdf и .txt (как я делал с OpenGL, пока не понял что есть .xml) и потом исправлять за парсером, опять же, фиксерами.


Прямо сейчас пункты 2 и 3 используют отдельный код, который, по сути, одинаковый с минимальными изменениями.

И вот делая это же третий раз (для групп), ещё и понимая что наверняка придётся ещё раз потом - я решил сделать промежуточный класс, в котором будет:

Ядро парсера

Это одна из вырезанных из примера вещей:

    protected static function ReadBlocks(lines: sequence of string; power_sign: string): sequence of (string, array of string);
    begin
      var res := new List<string>;
      var name: string := nil;
      
      foreach var l in lines do
        if l.StartsWith(power_sign) then
        begin
          if res.Count<>0 then
          begin
            yield (name, res.ToArray);
            res.Clear;
          end;
          name := l.Substring(power_sign.Length).Trim;
        end else
          res += l;
      
      if res.Count<>0 then yield (name, res.ToArray);
    end;
    protected static function ReadBlocks(fname: string) := ReadBlocks(ReadLines(fname), '#');

, словарь All и вся его свита… Ну и всякие дополнительные вещи, как WarnUnused/WarnAllUnused, которые тоже нужны в одинаковом виде для всех фиксеров.


А теперь внимание вопрос: Хоте ли вы действительно всё это знать?)))
То что у типа в примере имя не t1, t2, t3 / c1, c2, c3, а что то что явно что то значит, Fixer - уже говорит что это что то из реального кода, который мне реально понадобился.

Хотя было бы приятно, если бы вам действительно хотелось разобраться в том как я неправильно что то использую. Однако, честно, не верю и не поверю. Но в данном случае мне на-руку такая трата моего времени (почему - уже сказал).

И, хотите верьте, хотите нет - именно этого вопроса я ожидал и надеялся когда писал предыдущие 2 сообщения. Доказывать это я, разумеется, не собираюсь (даже если бы было возможно).

Покажите мне где это небезопасно, конкретно в примере с индексным свойством по умолчанию.

И в чём же там красивость? В лишнем where? Конкретно в данном примере where ни к чему. Вы его даже не используйте. Не надо мне в который раз рассказывать про какие то “фиксеры” - кратко, чётко и общо - вот что надо. Вы слишком углубляйтесь в детали реализации своих модулей и думаете, что другим:

  • они будут интересны (хотя, почему нет?)
  • будут понятны (всем ли?)

Не все так увлечены OpenGL/OpenCL, чтобы понимать Ваши примеры; если хотите, чтобы Вас понимали приводите более простые примеры, для “простых смертных”.

Мне бы хватило краткого объяснения зачем это нужно. Точнее:

  • Описание паттерна
  • Реализация (общий код, не привязываясь к конкретной задаче)
  • Когда нужен и когда без него нельзя обойтись

Ни первого, ни второго, ни третьего я так и не получил в чёткой форме, вместо - Вы на меня вылили гору текста. Посмотрели бы как описываются паттерны на refactoring.guru. Мне не нужно знать как и что Вы там реализуете, мне это неинтересно, мне главное было понять как, когда и зачем Ваш паттерн, который Вы называйте полезным, применять.

Я не называл конкретно Ваш пример абстрактный, я лишь сказал, что нужно объяснить на реальном примере зачем это всё нужно, не на каком-то куске кода без комментариев и не горой текста, которая выше.

В том что T это по задаче всегда фиксер. Иначе придётся as object as Fixer писать, что и есть небезопасно.

Ещё раз, это то что приятно о использовании общего базового типа. То, что можно написать одно индексное свойство и для каждого типа оно будет возвращать его же. where вообще не при чём.

Вы не поверите, но я не могу дать вам упрощённый вариант кода для того, что я ещё не написал.

Но основной смысл проще-некуда, этот код уже был выложен, только в ещё более упрощённой форме:

type
  Fixer<T> = abstract class where T: Fixer<T>;
    
    // всё остальное это общий интерфейс всех фиксеров,
    // второй раз его прописывать не вижу смысла
    protected procedure WarnUnused; abstract;
    
  end;
  
  GroupFixer = abstract class(Fixer<GroupFixer>)
    
    static constructor; // занимается заполнение словаря "All"
    
    // Group это, конечно, класс
    public procedure Apply(gr: Group); abstract;
    
    protected procedure WarnUnused; override :=
    // PackAll.pas подсвечивает в консоли слова как WARNING,
    // а потом это всё сохраняет в .log файл, так что достаточно
    Writeln($'WARNING: Fixer of group [{self.name}] wasn''t used');
    
  end;
  
begin end.

Ну а от GroupFixer уже наследуют все фиксеры для лично групп, каждый наследник хранит и выполняет какой то свой тип исправления.

Вот только в итоге это ничего сильно то не объясняет. Кроме того, вы сами попросили именно

Чужие проекты я искать не собираюсь, пустая трата времени. Позиция “всё что можно - сделано, а что не сделано - неправильно” сама фундаментально неправильная и спорить об этом я не буду.


Если что кратко по пунктам:

  1. Только что точнее некуда описал. Хотя в целом описание было давно. Документацией их не назовёшь, но это не то что я пытался дать.
  2. Её у меня нет, как и сказал, потому что ещё не написал. Есть только продублированный код, от которого я как раз избавляюсь этим паттерном. Или пытаюсь.
  3. Несколько раз сказано, надо для избежания дублей кода.

А в общем случае что это должно быть? Посмотрите refactoring.guru и охарактеризуйте Ваш “фиксер” примерно теми же терминами, что используют там в UML-схемах, иначе непонятно.

Можно было бы и написать, если хотели бы, чтобы Ваш паттерн поняли.

Уже в который раз слышу подобную фразу. Признайтесь - лень. И каждый раз Вы пытаетесь выкрутиться подобными шаблонными фразами, не надоело ли?

Можно было бы и лучше. Что такое этот Ваш “фиксер” - мне до лампочки. От того, что Вы будете часто говорить это слово я понимать лучше не стану. Посмотрите как сделаны UML-схемы - чётко, кратко и понятно и описание к ним такое же. Сделали бы также, плюс код и всё было бы хорошо.

Паттерны многие для данной цели существуют. Это не ответ.

Общий случай паттерна придуманного для фиксера ограничивается фиксером. Не надо пытаться абстрагироваться ниже пола.

Ещё 2 абзаца (не в том же порядке) бесполезны по этой же причине.

Это мне не нужно.

В issue я кинул обрезанный кусок своего кода, только чтоб напомнить что вообще где то там такой паттерн у меня реально использовался. По моему опыту, если написать больше - разработчики могут проигнорировать. Ну а подробнее написать всегда можно, если вдруг окажется мало.

А что до вас - я потратил ваше время при очередной попытке вами потратить моё время, по не стОящей того теме. И получил от этого пользу. К примеру, получил прекрасный приятный повод остаточно перечислить где этот паттерн мне понадобится. Ну и + так в целом по мелочам

(с новой стороны*)

Очень жаль что вы всё ещё не пытаетесь отойти и посмотреть на всю ситуацию со стороны, не смотря на все намёки (если их даже можно так назвать, при их прямоте).

Абстракция это обобщение, так что про пол - это неверная аналогия.

Тогда незачем кидать какой-то кусок кода, даже не удосужившись по-нормальному объяснить что да как, в общем случае, если это паттерн.

Неудивительно если проигнорируют, потому что Вы очень “постарались” им доходчиво объяснить нужный Вам паттерн. Не умеете нормально объяснять - пеняёте только на себя. Здесь Вас не будут учить объяснять, не умеете - учитесь, не хотите - извините. Вы не заставите разработчиков копаться в Ваших “фиксерах”, но Вы можете доходчиво им объяснить в общем случае и кратко проблему, чтобы они поняли. Остаётся одно - Ваше желание.

Вы переходите на более общий и низкий уровень, чем надо. Ниже пола.

Раз уж надо всё так разжёвывать - низкий уровень не в том же смысле что низкоуровневое программирование.

Надо кому? Вам? Потому что Вам не хочется обобщать? Тогда уж извините, то если разработчики проигнорируют Ваш комментарий, то я буду с ними полностью солидарен, хотя считаю, что это несколько жёстоко по отношению к пользователю запрещать что-то просто потому что он не потрудился и не изложил хорошо свою мысль. Я бы не закрывал подобные вещи, а требовал бы от автора-тестировщика по-нормальному изложить свою мысль и ждал бы сколько нужно. Здесь не телепаты, не будут за Вас догадываться что-же Вы хотите сказать. Тем более, Вы сами вызвались тестировать данный проект и уже являетесь фактически его тестировщиком и Вам как никому другому надо уметь правильно выражать свои мысли и обобщать.

Это только в NET Core

Ещё раз о том, что не надо пытаться ожидать, что в PascalABC.NET будет как C#. Это разные языки, хоть и на одной платформе… Кто хочет C# с begin-end - пусть идут на C#, тут - Паскаль.

В ядре ничего не правилось. Это проблема локального компьютера. Issue закрыта.