Всем привет, в своей работе (а ранее и в учебе) я часто использую C# и этот язык стал мне, что называется, привычным. Решив немного потренироваться в программировании, я написал консольный текстовый редактор для Linux. Вкратце опишу полученный опыт.

В Mono класс System.Console поддерживается не полностью, но Mono замечателен тем, что также как и в .NET можно использовать P/Invoke, для вызова методов нативных C/C++ Linux библиотек.

В моем случае это была библиотека ncurses. Выглядит это так:

public class Curses
    {        
        /// <summary>
        /// файл подключаемой библиотеки
        /// </summary>
        const string NCurses = "libncursesw.so.5";
        /// <summary>
        /// указатель на окно терминала
        /// </summary>
        private IntPtr window;
        [DllImport(NCurses)]
        private extern static IntPtr initscr();
        /// <summary>
        /// конструктор класса обертки
        /// </summary>
        public Curses()
        {
            window = initscr();
        }
        [DllImport(NCurses)]
        private extern static int endwin();
        /// <summary>
        /// Деструктор обертки
        /// </summary>
        ~Curses()
        {
            int result = endwin();
        }
        [DllImport(NCurses)]
        private extern static int mvwprintw(IntPtr window, int y, int x, string message);
        /// <summary>
        /// Вывод сообщения по координатам
        /// </summary>
        public int Print(int x, int y, string message)
        {
            return mvwprintw(window, y, x, message);
        }
        [DllImport(NCurses)]
        private extern static int refresh(IntPtr window);
        /// <summary>
        /// Обновление окна терминала
        /// </summary>
        public int Refresh()
        {
            return refresh(window);
        }
	. . .

Все методы ncurses в этом проекте подключались по мере необходимости, в случае если System.Console работал не должным образом, но если же стандартный метод работал это значительно облегчало жизнь.

Например метод ncurses wgetch() получает два байта вместо одного для символов Юникода, и для кириллической раскладки мне пришлось бы использовать более сложный метод с передачей указателя. Использование стандартного Console.ReadKey позволило оставить весь код управляемым и легко разделить функциональные клавиши от текстовых символов.

С чтением и записью файлов также не возникло проблем и все работало штатно как и на .NET

image

Когда уже все стабильно работало, была предпринята попытка собрать этот блокнот на Windows. Дабы не собирать PDCurses из исходников, я взял готовую dll из состава MinGW, а именно libpdcursesw.dll и она хорошо справилась в качестве замены ncurses.

Для сборки блокнота мне даже не понадобился Mono, я просто открыл решение MonoDevelop в Visual Studio без всяких приключений.

Единственное что потребовало корректировки, это вывод в консоль, мне пришлось отказаться от mvwprintw и я переписал Print, вывод заработал а изменения в коде были минимальны:

public void Print(int x, int y, string message)
        {
            Console.SetCursorPosition(x, y);
            Console.Write(message);            
        }

Также пришлось подкорректировать размеры рабочей области, и методы изменения цвета текста.

Все это убедило меня в том, что C# вполне годится для задач под Linux и решения в случае чего легко портировать на Windows.

Полная реализация доступна на GitHub, ветка master для Linux и windows для Windows.

Комментарии (16)


  1. staticmain
    25.09.2019 11:38

    public class Curses
        {        
            /// <summary>
            /// файл подключаемой библиотеки
            /// </summary>
            const string NCurses = "libncursesw.so.5";
            /// <summary>
            /// указатель на окно терминала
            /// </summary>
            private IntPtr window;


    А это какой-то встроенный тип документации? Выглядит максимально неоптимально и нечитаемо относительно Doxygen:
    public class Curses {        
        const string NCurses = "libncursesw.so.5"; /**< файл подключаемой библиотеки */
        private IntPtr window;                     /**< указатель на окно терминала  */


    1. Qautomat Автор
      25.09.2019 11:43
      +1

      summary отображается в IDE как всплывающая подсказка если навести курсор мыши на название


      1. staticmain
        25.09.2019 11:44
        -2

        Еще одна причина не использовать MonoDevelop. Это же максимально нечитаемо выглядит в коде.


        1. Qautomat Автор
          25.09.2019 11:51

          в Visual Studio оно так же работает, pastenow.ru/d2cf8ae9f6762fcf2f3710c965f7a747
          можно не проваливаться в метод, а просто прочитать там где он вызывается


          1. staticmain
            25.09.2019 12:10
            -1

            Не, ну серьезно? Люди сейчас яростно с пихом в скор и карму доказывают, что XML-комментарии лучше, чем двусимвольная метка? Чисто потому, что MS решили создать очередной собственный велосипед?


            1. Qautomat Автор
              25.09.2019 12:22

              кто как привык, я привык к тем требованиям, которые на работе приняты


            1. Doomsday_nxt
              25.09.2019 12:45
              +1

              Кроме собственно описания элемента, xml-комментарии также могут содержать дополнительные сведения. Например, описания параметров метода, описания возвращаемого значения, ссылки на другие типы, ссылки на документацию. А еще из xml-комментариев генерируются файлы xml-документации которые кладутся рядом с библиотекой и при использовании библиотеки в другом проекте — вы получите нативные подсказки.
              habr.com/ru/post/41514

                  /// <summary>
                  /// Description for SomeMethod.
                  /// </summary>
                  /// <param name="s"> Parameter description for s goes here.</param>
                  /// <seealso cref="System.String">
                  /// You can use the cref attribute on any tag to reference a type or member 
                  /// and the compiler will check that the reference exists.
                  /// </seealso>
                  public void SomeMethod(string s)
                  {
                  }
              


              1. staticmain
                25.09.2019 13:16

                Ну то есть как doxygen, но не как doxygen, ибо NIH:

                /*! \brief Информация о соединении */
                struct fen_connect {
                    bxi_ipv4 ipv4; /**< IP адрес подключения    */
                    u16      port; /**< Порт подключения        */
                    char *   host; /**< Имя хоста               */
                    bool     ssl;  /**< Соединение защищено SSL */
                };
                
                /*! \brief Клиентское соединение */
                struct fen_client {
                    frs_socket_t       socket;  /**< Текущий сокет */
                    struct fen_connect connect; /**< Текущее соединение */
                    struct fen_route   route[FEN_ROUTES_COUNT]; /**< Данные по направлениям */
                };
                
                /*!
                 * \brief Возвращает роут получения из клиента
                 * \param[in] client Клиент
                 * \return Роут получения
                 */
                static inline struct fen_route * fen_in(struct fen_client * client) {
                    return client ? &client->route[FEN_ROUTE_RECV] : NULL;
                }


                1. Doomsday_nxt
                  25.09.2019 13:23
                  +1

                  Честно, имхо, выглядит ужасно… Хотя, не спорю, возможно дело привычки…


            1. Xandrmoro
              25.09.2019 16:34

              Строго лучше, потому что зачем лезть чёрте-куда в исходники, которых может не быть, вместо того чтобы увидеть красивую всплывающую подсказку в автокомплите?


  1. Doomsday_nxt
    25.09.2019 13:18
    +1

    А зачем в линуксе mono, когда есть dotnet core?


    1. Qautomat Автор
      25.09.2019 14:04

      единственный плюс, который приходит на ум, это GTK# и конструктор форм для него
      … вспоминается еще MonoGame, но я про него ничего сказать не могу, кроме того что игровой движок, на котором сделано пара проектов


      1. more_cry
        26.09.2019 06:42

        MonoGame таргетит netcoreapp на OpenGL бекенде.



  1. a-tk
    26.09.2019 19:17
    +1

    Автор, у Вас точно C# привычный язык, а не С++?
    Странно в коде видеть финализатор (который совсем не то же самое, что в С++ деструктор) и полностью проигнорированный IDisposable. Отсутствие инкапсуляции, каких-то более-менее пригодных к работе абстракций и т.п.
    Ну да, в Mono тоже есть p/invoke. И это единственный посыл статьи?..


    1. Qautomat Автор
      27.09.2019 14:49

      обертки сделаны по рекомендациям учебника по Mono, проект just4fun, что отражено в названии. Для реализации хватило стандартных типов. Наиболее полной реализацией ncurses считаю mono-curses от migueldeicaza