Страницы

Поиск по вопросам

Показаны сообщения с ярлыком gcc. Показать все сообщения
Показаны сообщения с ярлыком gcc. Показать все сообщения

четверг, 9 апреля 2020 г.

Компиляция 7-zip c помощью MSYS2

#cpp #c #gcc

                    
Подскажите, как компилировать 7-zip c помощью MSYS2
    


Ответы

Ответ 1



Подробная инструкция по компиляции с помощью MSVC доступна здесь. Добавлю вариант с компиляцией через Cygwin: Скачиваем Cygwin: выбираем версию x32. Устанаваливаем, например, в директорию C:\projects\tools\cygwin. Из доступных пакетов понадобятся: make, gcc/g++, wxwidgets. Соглашаемся с установкой предложенных пакетов. Скачиваем нужную версию 7zip под POSIX-системы: sourceforge. Распаковываем, например, в директорию C:\projects\other\7zip\source. Запускаем Cygwin.bat, переходим в директорию, где распакована библиотека 7zip: cd /cygdrive/c/projects/other/7zip/source. В директории с исходниками есть множество makefile для различных платформ, выбираем makefile.cygwin и копируем содержимое в файл makefile.machine. Выполняем make -j4 для компиляции цели all. Редактируем файл install.sh для указания своего пути установки скомпилированных файлов. Находим параметр DEST_HOME и изменяем его, например, так: /cygdrive/c/projects/other/7zip/build. Выполняем ./install.sh Теперь в каталоге /bin расположен исполняемый файл 7za.exe. Для его запуска копируем следующие библиотеки: cyggcc_s-1.dll, cygstdc++-6.dll, cygwin1.dll, из каталога cygwin_install_dir/bin. 7zip готов к использованию

воскресенье, 15 марта 2020 г.

Возможно ли “виртуальное клонирование” в С++?

#cpp #gcc


Наткнулся в "More Exceptional C++" на информацию, что виртуальные методы могут вместо
указателей на объекты базового класса возвращать объекты перегруженного класса. Решил
попробовать смоделировать "самоопределяющийся" объект - с виртуальным методом, вместо
указателя базового класса возвращающим указатель на объект производного, чтобы можно
было вызывать функции, отсутствующие в базовом классе. Вот пример с "виртуальным клонированием":
#include 
#include 

class Base
{
public:
    Base(){}
    virtual ~Base()=0;
    virtual Base*   clone()
    {
        qDebug()<<"Base clone";
        return 0;
    }
};

Base::~Base(){}

class Derived: public Base
{
public:
    Derived(){}
    virtual Derived*   clone()
    {
        qDebug()<<"Derived clone";
        return new Derived(*this);
    }
    QString derivedOnly(){return "derivedOnly";}
    virtual ~Derived(){}
};

int main(int argc, char *argv[])
{
    Derived d1;
    Base *  b1=&d1;
    Base *  b2=b1->clone();     b2=b2;
    Derived * d2=d1.clone();    delete d2;
//  Derived * d3=b1->clone();   delete d3;
}

Так всё работает, и вывод qDebug показывает, что запускается клонирование производного
класса. Однако, раскомментировав последнюю строку, при компиляции получаю сообщение
об ошибке "invalid conversion from 'Base' to 'Derived' [-fpermissive]".
Выводы:

Очевидно, что компилятор (g++, Windows/MinGW) проигнорировал описание Derived * Derived::clone(),
сгенерировав вместо него код для Base * Derived::clone().
Эту подмену транслятор сделал, никак меня не уведомив. Я рассчитываю получить указатель
типа Derived, а получаю Base - и ничего об этом не знаю! 
Я понимаю, для чего это сделано. Я мог бы написать что-нибудь типа b1->clone()->derivedOnly(),
и транслятор не знал бы, допустима ли такая конструкция, так как статически неизвестно,
будет ли при исполнении b1 указывать на Base или на Derived.

Вопросы:

Почему так происходит - является ли это частью стандарта (где можно посмотреть?)
или самоуправством g++ (4.6.2)?
Можно ли сделать так, чтобы выдавался warning?
Ведут ли себя так же другие трансляторы?
Есть ли возможность решить изначальную задачу, то есть "развернуть" объект из указателя
на базовый класс, не указывая, в какой именно класс мы его разворачиваем (т. е. без
dynamic_cast'ов)?
    


Ответы

Ответ 1



О выводах Выводы неверные. Пункт 3 верный. Он же является ответом на Ваш вопрос. А вот пункт 1 и, следовательно, 2 - неверные. Derived* Derived::clone(); Base* Base::clone(); Какой из методов Вы бы выбрали, будь у Вас указатель на тип Base*? Ответы 1) Вы же сами отвечаете себе на этот вопрос в выводе 3: статически неизвестно, будет ли при исполнении b1 указывать на Base или на Derived Это приводит к тому, что компилятор использует статический тип объекта, то есть Base*. У объекта такого типа метод Base* clone() возвращает Base*, чтобы привести его к Derived* нужно явное преобразование. Ссылка на стандарт: раздел 10.3.8: [...] its result is converted to the type returned by the (statically chosen) overridden function (5.2.2) 2) Тут я поразмышляю о другом warning'е. Который не будет показан, если написать, например: Derived* d2 = dynamic_cast( b1->clone() ); Явное преобразование static_cast( b1->clone() ) это как бы уже warning. Страуструп пишет в "Дизайн и эволюция C++", что намеренно завел вместо лаконичного, но скрытного синтаксиса приведения типов C, громоздкий и неудобный, привлекающий внимание синтаксис приведения типов в стиле C++ (с _cast<>'ами). Кто Вам гарантирует, что b1 всегда будет указывать на Derived-объект? Только Вы сами и гарантируете. За этот архитектурный "косяк" (см. п.4) Вы и расплачиваетесь dynamic_cast'ом. Или, если производительность важна, static_cast'ом, подкладывая себе мину замедленного действия. Относитесь к явным преобразованиям типов, как к warning'ам, ругающим Вашу архитектуру. 3) да. См. п. 1 Например, VS2013: error C2440: 'initializing' : cannot convert from 'Base *' to 'Derived *' Cast from base to derived requires dynamic_cast or static_cast 4) Итак, Вы хотите реализовать виртуальный метод клонирования. Сам метод реализован правильно, и работает правильно. Неправильно Вы используете результат клонирования - считаете, что там какой-то определенный потомок. На самом деле, имея указатель Base*, Вы можете полагаться только на интерфейс класса Base, никаких QString derivedOnly(). Как же быть? Вариант 0. Вы точно знаете, что Base* в данном контексте - это Derived*. Тогда Вы можете либо сразу указать тип Derived*, либо сделать dynamic_cast. Вариант1. Код оперирует указателем Base* и неизвестно, на какой из наследников есть указатель: Derived1* или Derived2*. Вы клонируете объект, думая, что там Derived1, а на самом деле не он. Что тогда? Логика программы развалилась. Из-за того, что Вы полагаетесь на различный интерфейс сущностей, которые по контракту все имеют единый интерфейс Base*.

Объясните все возможные причины использования заголовков в мультифайловых проектах С

#c #gcc


Посмотрите в начало стр. 5 здесь и объясните популярно, зачем в принципе нужен заголовок
calc.h, если проект спокойно компилируется и запускается без заголовков вообще через: 
Andrey@AsusP5B ~/2
$ gcc driver.c calc.c -o prog

Andrey@AsusP5B ~/2
$ ./prog

Square of 5 is 25

Andrey@AsusP5B ~/2
$

Это что, новым компиляторам уже на заголовки наплевать или на прототипы функций,
или это чистота написания кода, или просто лучше заголовки лучше сразу создавать, чтобы
не было путаницы или х.з., так как в будущем они понадобятся для других целей... В
теории, ПОЧЕМУ?    


Ответы

Ответ 1



Все работает, потому что си компилятор полагается на то, что Вы самостоятельно угадали сигнатуру. Возьмите driver.c, закомментируйте #include "calc.h" и "сделайте ошибку в вызове функции, где-то так: printf("\nSquare of %d is %d\n", x, square()); Код скомпилируется и даже никто не ругнется. Но вот только работать он будет странно (как именно странно - зависит от ситуации, может упасть, а может выводить странные результаты). У меня, к примеру, выводит просто "Square of 5 is 1". Правильные заголовочные файлы позволяют компилятору знать правильную сигнатуру функции и находить подобные ошибки. Но почему же оно все-таки компилируется? Да все просто. Когда компилятор видит объявление функции, он генерирует для нее код и запоминает адрес начала в специальной таблице в виде имя-адрес (да, там нет параметров!). Когда нужно вставить вызов функции, просто смотрит в таблицу и поставляет "call адрес". Сами параметры перед этим напихает в стек push'ем. Но тут есть ещё одна хитрость. На самом деле вначале туда проставляются не адреса. Адреса уже проставляет линковщик. Именно по этой причине порядок компиляции файлов не столь важен. Почему же оно не работает, хотя и компилируется? Вызывающая сторона в стек параметры добавляет, но их фактическое кол-во нигде не отмечает. Вызываемая сторона просто достает с стека их. Но опять же, она не может никак узнать, что там именно то, что положила вызывающая сторона. Поэтому, что достали с стека, то и обработали. В с++ этот трюк уже не проходит. Там компилятор делает "манглироване имен". То есть в таблицу функций вставляет имя функции с хитрым описанием типов параметров. Это нужно, так как в С++ есть возможность создать несколько функций с одним именем и разным кол-вом/типом параметров.

Ответ 2



В данном случае все работает без calc.h потому, что функция square() из calc.c возвращает int, а это значение результата функции по умолчанию. Измените в calc.c на double square (int x) и Вы увидите, как все сломается. Update Смотрите, вот код и результаты avp@avp-xub11:hashcode$ more cf1.c cf2.c | cat :::::::::::::: cf1.c :::::::::::::: #include int main () { int x = 5; const char *fmt = "square of %d is " #ifdef TINT "%d\n" #else "%f\n" #endif ; printf(fmt, x, square(x)); return 0; } :::::::::::::: cf2.c :::::::::::::: #ifdef TINT int #else double #endif square (int x) { return x * x; } avp@avp-xub11:hashcode$ gcc cf1.c cf2.c -DTINT avp@avp-xub11:hashcode$ ./a.out square of 5 is 25 avp@avp-xub11:hashcode$ gcc cf1.c cf2.c avp@avp-xub11:hashcode$ ./a.out square of 5 is -0.000000 avp@avp-xub11:hashcode$ Update 2 Добавим calc.h #ifndef _CALC_H // это так называемый guard, почти всегда нужен в заголовочных файлах #define _CALC_H #ifndef TINT double #else int #endif square (int); #endif который в зависимости от TINT определяет либо int либо double square(). avp@avp-xub11:hashcode$ more cf1.c cf2.c | cat :::::::::::::: cf1.c :::::::::::::: #include #include "calc.h" int main () { int x = 5; const char *fmt = "square of %d is " #ifdef TINT "%d\n" #else "%f\n" #endif ; printf(fmt, x, square(x)); return 0; } :::::::::::::: cf2.c :::::::::::::: #include "calc.h" #ifdef TINT #define CAST int #else #define CAST (double) double #endif square (int x) { return CAST x * x; } avp@avp-xub11:hashcode$ gcc cf1.c cf2.c -DTINT avp@avp-xub11:hashcode$ ./a.out square of 5 is 25 avp@avp-xub11:hashcode$ gcc cf1.c cf2.c avp@avp-xub11:hashcode$ ./a.out square of 5 is 25.000000 avp@avp-xub11:hashcode$ И все, как видите, заработало. Конечно, в простых случаях (а на практике лучше все делать проще (это я тут от лени начал ваять шаблоны (templates))) никакой препроцессор (для условной компиляции, конечно же) не нужен.

Не линкуются вместе загрузчик и ядро ОС

#gcc #сборка #os #osdev #ld


Я пытался написать примитивную ОС по урокам "Bare Bones" на osdev wiki. Проблема
заключается в том, что даже на самом начале ld выдает ошибку, что главная функция ядра,
_kmain, не определена. Итак, вот код:  

kernel.cpp  

    void _kmain() {  
        return;  
    }


boot.asm  

    ; This file is loaded by GRUB, Protected Mode is already enabled

    ; Multiboot constants

    MBALIGN equ 1<<0
    MEMINFO equ 1<<1
    FLAGS equ MBALIGN | MEMINFO
    MAGIC equ 0x1BADB002
    CHECKSUM equ -(MAGIC+FLAGS)

    ; Multiboot header

    section .multiboot
    align 4 ; Yes, I know, it's not necessary
        dd MAGIC
        dd FLAGS
        dd CHECKSUM

    ; Bootloader stack (we'll use another stack for OS, of course)

    section .bootstrap_stack, nobits
    align 4
        stack_bottom:
            resb 16384 ; 16K of stack, seems enough for a bootloader
        stack_top:

    section .text
        global _start
        _start:
        mov esp, stack_top ; Stack grows in a backwards direction
        extern _kmain
        call _kmain
        cli ; If _kmain returns, we will halt the computer
    .halt:
        hlt
        jmp .halt


Makefile

    ASM=nasm
    ASM_FLAGS=-felf32
    CC=/home/alexander/opt/cross/bin/i686-elf-g++
    CFLAGS=-ffreestanding -Wall -Wextra
    LINKER=/home/alexander/opt/cross/bin/i686-elf-gcc
    LFLAGS=-ffreestanding -nostdlib -lgcc -T linker.ld

    all: boot.o kernel.o
        $(LINKER) $(LFLAGS) -o kernel.bin $^
    boot.o:
        $(ASM) $(ASM_FLAGS) boot.asm -o boot.o

    kernel.o:
        $(CC) $(CFLAGS) -c kernel.cpp -o kernel.o

    screen.o:
        $(CC) $(CFLAGS) -c screen.cpp -o screen.o

    clean:
        rm *.o

    rmbaks:
        rm *~


linker.ld

    ENTRY(_start)
    SECTIONS {
        . = 1M;
        .text BLOCK(4K) : ALIGN(4K) {
            *(.multiboot)
            *(.text)
        }
        .rdata BLOCK(4K) : ALIGN(4K) {
            *(.rdata)
        }
        .data BLOCK(4K) : ALIGN(4K) {
            *(.data)
        }
        .bss BLOCK(4K) : ALIGN(4K) {
            *(COMMON)
            *(.bss)
            *(.bootstrap_stack)
        }
    }


Вывод после запуска make:

nasm -felf32 boot.asm -o boot.o
/home/alexander/opt/cross/bin/i686-elf-g++ -ffreestanding -Wall -Wextra -c kernel.cpp
-o kernel.o
/home/alexander/opt/cross/bin/i686-elf-gcc -ffreestanding -nostdlib -lgcc -T linker.ld
-o kernel.bin boot.o kernel.o
boot.o: In function `_start':
boot.asm:(.text+0x6): undefined reference to `_kmain'
collect2: error: ld returned 1 exit status
make: *** [all] Ошибка 1


P.S. В screen.cpp всякие полезные функции для работы с дисплеем, но он не используется.
    


Ответы

Ответ 1



На самом деле все просто упирается в компилятор. У Вас g++ (а не gcc). По умолчанию g++ формирует имена функций (те, что мы можем увидеть командой nm kernel.o) с учетом типа функции и параметров. Так, вместо ожидаемого _kmain, в .o получается _Z6_kmainv. Если Вы все еще хотите продолжать упражнения с С++, то придется явно сказать компилятору, что нужны имена функций в "сишном стиле". Для этого достаточно написать прототипы функций в специальном блоке: extern "C" { void _kmain(void); // это наш случай }; Если хотите, чтобы код без изменений (конечно, остальные его части тоже должны быть совместимы как с Си, так и с C++) компилировался также и gcc, то придется добавить немного директив препроцессора (к счастью он одинаков для g++/gcc) #ifdef __cplusplus extern "C" { #endif void _kmain(void); ... #ifdef __cplusplus }; #endif

понедельник, 9 марта 2020 г.

Ввод данных в С. Не определяется GetString() из cs50

#c #gcc #make #cs50


Почему у меня не определяется string s = GetString();?

hell.c

#include 
#include 

int main(void)
{
    printf("State our name: ");
    string s = GetString();
    printf("hello, %s\n", s);
}



  cc hell.c -o hell

/tmp/ccHZXs2m.o: In function `main'
hell.c:(.text+0x16): undefined reference to `GetString'
collect2: error: ld returned 1 exit status
make: *** [hell] Error 1


    


Ответы

Ответ 1



Почему бы тебе не воспользоваться методом scanf() для чтения с консоли? int main(void) { string name; printf("Введите ваше имя"); scanf("%s", name); printf("Привет, %s\n!", name); return 0; }

Ответ 2



Нужно скомпилировать и слинковать с файлом cs50.c cc -c help.c -o help.o cc -c cs50.c -o cs50.c cc -o help help.o cs50.o

Ответ 3



Потому что у тебя нету такой функции GetString(). Добавь например выше.. string GetString(void) { return "test"; }

Ответ 4



Могу предположить, что у вас слетели или не были настроены переменные среды CFLAGS и LDLIBS. Попробуйте создать в этой же папке файл с названием makefile и поместить в него данные строки: CC=clang CFLAGS=-ggdb3 -O0 -std=c99 -Wall -Werror LDLIBS=-lcs50 -lm После чего просто вызовите make: make hell for ABC ;)

Ответ 5



Потому что cs50 это библиотека курса гарварда cs50. А у Вас она не установлена. Запустите в их удаленной линуксовой среде и там подцепится.

Ответ 6



Используйте консоль в редакторе (отдельная иконка рядом с gedit). Тогда независимо от того, подключите ли вы библиотеку string.h или нет, функция GetString сработает.

Ответ 7



.h файл только декларирует список функций, для их использования в программу должен быть вкомпилирован их код находящийся скорее всего в .cшных файлах (но может оказаться в .asm .haskell ... .lib .dll .so .a) в самом простейшем случае у вас где-то должен лежать файл cs50.c, который тоже нужно скомпилировать: gcc -o ./hello[.exe] hello.c cs50/lib/src/cs50.c PS: описание принципа использования модулей, раздельной компиляции и использования ключевого слова static неплохо объяснено в курсе Евгения Линского

Ответ 8



В окружении CS50 на c9.io начиная с 2016 эту функцию переименовали в: get_string() Используйте её или найдите более свежие видео-лекции.

Ответ 9



Потому, что ты не читал нормально начало к заданиям - ТАМ ТАК ЖИРНО написано ЦТ Дополнительные материалы reference.cs50.net/ — разъяснение функций библиотек, используемых во время обучения. На английском. http://computer.howstuffworks.com/c.htm стр. 11 – 14 и 39 И вот если пройти в разъяснение функций библиотек - и поискать поиском string - то будет чудо, выяснится что надо подключать библиотеку string.h - а она не прописана в cs50 - ребята не на все делали костыль! Зачем костыль на библиотеку готовую?

воскресенье, 8 марта 2020 г.

Компилятор не поддерживает расширенную инициализацию

#cpp #gcc #cpp11 #инициализация


Имеется такой код:

int main() {
    double d {4.5};
    int x {d};
}


Прочитал, что в новой версии С++11 есть поддержка на запрет сужающих преобразований
при инициализации. Но компилятор ошибки не выдает, хотя должен (ибо я пытаюсь double
в int преобразовать с синтаксисом новой версии инициализации (с помощью фигурных скобок),
которая как раз запрещает подобные небезопасные преобразования. 

IDE - DevC++ 5.11
Компилятор - TDM-GCC 4.9.2.

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


Ответы

Ответ 1



Стандарт С++ не обязывает реализацию языка выдавать именно "ошибку компиляции" для случаев когда в стандарте написано что код ошибочен. (Однако при этом не должно ломается SFINAE, иначе это уже баг компилятора). Компилятор g++ в некоторых случаях ограничивается предупреждением: double d = 4.5; int x{d}; // предупреждение в g++ int y{4.5}; // ошибка в g++ Впрочем этого достаточно, т.к. рекомендуется компилировать код с -Wall -pedantic -Werror. Что касается стандарта, то int x{d}; это несомненно неправильный код. В разделе [dcl.init.list] есть аналогичный пример.

Как корректно указать дополнительный путь для gcc?

#linux #c #gcc


Я новичек в этом. Я использую Linux и gcc в bash.

Имеется:


p1.c:

int func()
{
  return 1889; 
}

func.h:

#ifndef ONCE
#define ONCE
int func();
#endif

p2.c:

#include 
#include 

int main()
{
    int a = func();
    printf("%d",a);
    return 0;
}



func.h я специально переместил в другую папку. В *.bashrc* дописал export PATH="$PATH:/root/deleteme/spec"

# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/root/deleteme/spec




# gcc p?.c


вывод:


p2.c:2:18: fatal error: func.h: No such file or directory
 #include 
                 ^
compilation terminated.



Заработало при указании gcc -I. 

Почему не работает через переменную окружения? Хочется разобраться как это работает.
Спасибо :)
    


Ответы

Ответ 1



Переменная окружения $PATH, содержащая пути где исполняемые файлы (скрипты, программы) лежат, не имеет отношения к путям, в которых gcc ищет заголовочные файлы (*.h). Есть директории по умолчанию такие как /usr/include, например, используемые для библиотек, установленных из *-dev системных пакетов. Распространенный способ указать свои пути к заголовочным файлам это использовать -I опцию. Если хочется, можно определить соответствующие переменные окружения (CPATH, etc). Если использовать "func.h", а не , то func.h сперва ищется в директории с текущим файлом (p2.c). Существуют и другие (менее используемые) опции, контролирующие как заголовки ищутся.

среда, 26 февраля 2020 г.

Где найти свежую документацию по gnu c?

#c #gcc #gnu #c11


По запросу в Гугле - выдает страницу https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html
в которой ничего не говориться о c 11, на официальном сайте я ничего подходящего не нашел. 

Где можно найти актуальный мануал по gnu c 11?
    


Ответы

Ответ 1



Официально этот документ называется так: INTERNATIONAL STANDARD ©ISO/IEC ISO/IEC9899:201x Найти его можно, например, здесь: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1539.pdf

Ответ 2



Прочтите, пожалуйста, вот этот документ. Особое внимание обратите на данный абзац: By default, GCC provides some extensions to the C language that, on rare occasions conflict with the C standard. See Extensions to the C Language Family. Some features that are part of the C99 standard are accepted as extensions in C90 mode, and some features that are part of the C11 standard are accepted as extensions in C90 and C99 modes. Use of the -std options listed above disables these extensions where they conflict with the C standard version selected. You may also select an extended version of the C language explicitly with -std=gnu90 (for C90 with GNU extensions), -std=gnu99 (for C99 with GNU extensions) or -std=gnu11 (for C11 with GNU extensions). Ссылку на описание GNU C extensions на всякий случай оставляю здесь: https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html

вторник, 25 февраля 2020 г.

gcc линковщик выдаёт undefined reference to 'sin'

#c #gcc


Компилирую следующий код:

#include 
#include 
#include 

double f(double x) {
        return sin(x);
}

int main(int argc, char** argv) {
        return 0;
}


Вылезает ошибка линковщика:


  /tmp/ccWto7Ot.o: In function f': temp.c:(.text+0x13): undefined
  reference tosin' collect2: ld returned 1 exit status


Никак не могу понять где я накосячил. Перебрал весь код, приведённый пример является
минимальным для воспроизведения ошибки. Компилирую без опций, т.е. просто


  gcc test.c

    


Ответы

Ответ 1



Компилирую без опций, т.е. просто $ gcc test.c а надо как минимум подключить библиотеку libm.so, для чего следует указать опцию -lm: $ gcc -lm test.c или, например, так: $ make test CFLAGS=-lm

четверг, 13 февраля 2020 г.

Что является аналогом DllMain в Linux?

#linux #gcc #разделяемые_библиотеки


Функция DllMain является дополнительной точкой входа в динамически подключаемую библиотеку.
BOOL WINAPI DllMain(
  __in  HINSTANCE hinstDLL,
  __in  DWORD fdwReason,
  __in  LPVOID lpvReserved
);

Каким образом можно в linux задать код, который будет подобным образом выполняться
при загрузке и выгрузке разделяемой библиотеки?    


Ответы

Ответ 1



Задать код, который будет выполняться при подключении и отключении библиотеки, можно, используя атрибуты gcc constructor и destructor. void start() __attribute__ ((constructor)); void finish() __attribute__ ((destructor)); void start() { // Выполнить до } void finish() { // Выполнить после }

Ошибка: 'a' was not declared in this scope

#cpp #gcc #mingw #qt


Скачал последний qt 5.3.0 c MinGW 4.8.2 и удивился. (тоже самое и с qt 5.2.0 с MinGW
4.8.0)
#include 

using namespace std;

template 
class Super{
    class A{
    protected:
        int a;
    };
    class B : public A{
        int b;
        B(){
            b = a;
        }
    };
};

int main()
{
    cout << "Hello World!" << endl;
    return 0;
}

Не работает. Выдаёт:
C:\***\main.cpp:14: ошибка: 'a' was not declared in this scope
             b = a;
                 ^

Хотя, убрать строку template , то всё работает.
Причём приведённый выше код без изменений работает, например, в Visual Studio 2008.
Это баг MinGW или же это так и надо/допустимо? Кстати, нормально работает с MinGW и
если писать this->a.    


Ответы

Ответ 1



Кажется, я понял. Вот тут обсуждают похожую проблему. В вашем случае ситуация такова. b является зависимым именем. Компилятор должен понять значение этого зависимого имени до подстановки T. (Посмотрите детали и обсуждение в этом вопросе). Поскольку он не может, разработчики стандарта решили убрать зависимые имена из области видимости внутри шаблона. Поэтому вам приходится указывать явно, что именно вам нужно. Таким образом, баг в старом Visual Studio, их реализация не вполне соответствовала стандартам. (В новом Visual Studio уже соответствует.) Я считаю, что язык C++ неоправданно избыточно сложен. Вы натолкнулись на одно из таких мест.

Ответ 2



При выполнении поиска неквалифицированных имен (unqualified name lookup), в рассмотрение не включаются базовые классы, зависящие от параметров текущего шаблона. В данном случае A - это на самом деле Super::A и зависит от параметра шаблона T. По этой причине имя a из базового класса A не находится компилятором. Использование квалифицированого имени или явного синтаксиса доступа к члену класса устранит ошибку int b = A::a; int b = this->a; Это правило связанно с поиском неквалифицированных имен именно в базовых классах, а не с зависимыми именами вообще. Эта деталь почему-то не затронута в принятом ответе.

Оптимальные опции для x86 GCC (Windows)

#cpp #c #gcc


Использую проект AdvanceCOMP.
Возник вопрос, как можно увеличить производительность данного проекта при компиляции.

Сейчас я делаю так:

CFLAGS='-Ofast -flto' CXXFLAGS='-Ofast -flto' LDFLAGS='-static -flto' ./configure
make


Справка из ./configure -h

`configure' configures AdvanceCOMP 1.20 to adapt to many kinds of systems.

Usage: ./configure [OPTION]... [VAR=VALUE]...

To assign environment variables (e.g., CC, CFLAGS...), specify them as
VAR=VALUE.  See below for descriptions of some of the useful variables.

Defaults for the options are specified in brackets.

Configuration:
  -h, --help              display this help and exit
      --help=short        display options specific to this package
      --help=recursive    display the short help of all the included packages
  -V, --version           display version information and exit
  -q, --quiet, --silent   do not print `checking ...' messages
      --cache-file=FILE   cache test results in FILE [disabled]
  -C, --config-cache      alias for `--cache-file=config.cache'
  -n, --no-create         do not create output files
      --srcdir=DIR        find the sources in DIR [configure dir or `..']

Installation directories:
  --prefix=PREFIX         install architecture-independent files in PREFIX
                          [/usr/local]
  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
                          [PREFIX]

By default, `make install' will install all the files in
`/usr/local/bin', `/usr/local/lib' etc.  You can specify
an installation prefix other than `/usr/local' using `--prefix',
for instance `--prefix=$HOME'.

For better control, use the options below.

Fine tuning of the installation directories:
  --bindir=DIR            user executables [EPREFIX/bin]
  --sbindir=DIR           system admin executables [EPREFIX/sbin]
  --libexecdir=DIR        program executables [EPREFIX/libexec]
  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
  --libdir=DIR            object code libraries [EPREFIX/lib]
  --includedir=DIR        C header files [PREFIX/include]
  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
  --infodir=DIR           info documentation [DATAROOTDIR/info]
  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
  --mandir=DIR            man documentation [DATAROOTDIR/man]
  --docdir=DIR            documentation root [DATAROOTDIR/doc/advancecomp]
  --htmldir=DIR           html documentation [DOCDIR]
  --dvidir=DIR            dvi documentation [DOCDIR]
  --pdfdir=DIR            pdf documentation [DOCDIR]
  --psdir=DIR             ps documentation [DOCDIR]

Program names:
  --program-prefix=PREFIX            prepend PREFIX to installed program names
  --program-suffix=SUFFIX            append SUFFIX to installed program names
  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names

System types:
  --build=BUILD     configure for building on BUILD [guessed]
  --host=HOST       cross-compile to build programs to run on HOST [BUILD]

Optional Features:
  --disable-option-checking  ignore unrecognized --enable/--with options
  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
  --enable-silent-rules   less verbose build output (undo: "make V=1")
  --disable-silent-rules  verbose build output (undo: "make V=0")
  --disable-largefile     omit support for large files
  --enable-bzip2       Use the bzip2 compression
  --enable-valgrind       enable the use of valgrind in testing
  --enable-debug          enable debugging

Some influential environment variables:
  CC          C compiler command
  CFLAGS      C compiler flags
  LDFLAGS     linker flags, e.g. -L if you have libraries in a
              nonstandard directory 
  LIBS        libraries to pass to the linker, e.g. -l
  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I if
              you have headers in a nonstandard directory 
  CPP         C preprocessor
  CXX         C++ compiler command
  CXXFLAGS    C++ compiler flags
  TESTENV     Test environment

Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.

Report bugs to the package provider.
AdvanceCOMP home page: .

    


Ответы

Ответ 1



Флаг -Ofast не рекомендуется, так как считается небезопасным (включает оптимизации, нарушающие совместимость со стандартом). Есть куча разных флагов, но использовать их стоит в зависимости от целевой архитектуры и с умом. Я довольно часто использую следующий набор с целью получить максимальную производительность приложения на архитектуре x86: -march=native -O3 -mfpmath=both -funroll-loops -fopenmp -floop-parallelize-all -D_GLIBCXX_PARALLEL -D_GLIBCXX_MERGESORT Они не требуют вмешательства пользователя в код программы.

Ответ 2



Попробуйте OpenMP версию STL. Достаточно добавить флаг -D_GLIBCXX_PARALLEL и установить необходимые для OpenMP библиотеки, и все стандартные алгоритмы будут заменены на паралельные. Подробней тут: https://gcc.gnu.org/onlinedocs/libstdc++/manual/parallel_mode_using.html Попробуйте еще сборку под С++11. А так, конечно надо профайлить код.

Что означает -lstdc++ в C++?

#cpp #gcc #g++


Есть такая строчка в cmd:

gcc test.cpp -o test555 -lstdc++


Что означает -lstdc++ и как расшифровывается?
    


Ответы

Ответ 1



параметр -l говорит компоновщику подключить библиотеку. В данном случае - libstdc++. Библиотеки указываются без префикса"lib" Эту команду можно сократить, если использовать g++ g++ test.cpp -o test g++ подключает libstdc++ автоматически

воскресенье, 9 февраля 2020 г.

С (GCC). Как исключить из сборки неиспользуемые функции?

#c #gcc


В проекте есть пара .c / .h, в которой объявлены и определены несколько функций.
При этом некоторые из них не используются. Но после компиляции, заглянув в листинг
можно обнаружить там ассемблерный код этих самых неиспользуемых функций? Как исключить
их из сборки? В моем случае память очень ограничена и хотелось бы оптимально ее расходовать.
Да и вообще интересно. К тому же, не попадают же таким образом неиспользуемые ф-ии
из стандартной библиотеки.    


Ответы

Ответ 1



Итак давайте разбираться, на этапе компиляции понятное дело что функции вряд-ли куда денутся, все они окажутся в объектном файле, следовательно все дело в сборке (linking). Создадим пример: /* Файл func1.c */ int FuncA (void) { return 1; } /* ни где не используется */ int FuncB (void) { return 2; } /* ни где не используется */ /* Файл func2.c */ int FuncC (void) { return 0; } /* используется в main() */ /* Файл func1.h */ int FuncA (void); int FuncB (void); /* Файл func2.h */ int FuncC (void); /* Файл main.c */ #include "func1.h" #include "func2.h" int main(int argc, char **argv) { return FuncC(); } Теперь начинаем собирать: gcc -o a.out main.c func1.c func2.c Проверяем: nm a.out | grep Func[ABC] 080483c4 T FuncA 080483ce T FuncB 080483d8 T FuncC Ничего не получилось :-) Делаем еще одну попытку: gcc -c func1.c gcc -c func2.c gcc -o a.out main.c func1.o func2.o Проверяем: nm a.out | grep Func[ABC] 080483c4 T FuncA 080483ce T FuncB 080483d8 T FuncC Опять все функции попали в выходной файл. Применяем черную магию, а конкретно собираем библиотеку: gcc -c func1.c gcc -c func2.c ar rcs lib.a func1.o func2.o gcc -o main.c lib.a Проверяем: nm a.out | grep Func[ABC] 080483c4 T FuncC Итак у нас получилось. Отсюда вывод - разделяем исходняки на модули затем компилируем модули в объектные файлы, затем объектные файлы объединяем в библиотеку, а затем собираем основную программу с библиотеками подключатся только используемые модули UPDATE У gcc-шного линкера есть такая возможность как -gc-sections (garbage collect and discard unused sections), воспользоваться ею легко: gcc -fdata-sections -ffunction-sections -Wl,--gc-sections -o a.out \ main.c func1.c func2.c nm a.out | grep Func[ABC] 080483c0 T FuncC Но это не всегда приемлемо особенно когда это касается больших проектов. Пример

Ответ 2



Посмотрите вывод команды nm your.exe Код будет помечен метками T и t (t это "внутренний" (static) код). Часто несколько функций размещаются в одной единице компиляции (.c), которая (.o) линкуется с Вашим модулем либо непосредственно либо из библиотеки. Если Вы ссылаетесь на одну функцию из (.o), то остальные тоже прилинкуются. IMHO исключить их нельзя.

Ответ 3



Вот написал такой тестик #include __attribute__ ((noinline)) static int func_fff(int a) { puts ("OK"); return a*2; } int main() { printf ("%d\n", 4); #ifdef __COMP printf ("%d\n", func_fff (10)); #endif return 0; } Параметры компиляции и выводы программы nm : gcc test.c -c -o a.o nm a.o | grep func_fff t func_fff gcc test.c -c -o a.o -O nm a.o | grep func_fff gcc test.c -c -o a.o -O -D__COMP nm a.o | grep func_fff t func_fff Анализ ассемблерного кода также показывает, что во втором варианте никаких следов функции нет. Вывод: невключение невызываемой функции на этапе компиляции можно достичь, определяя ее как static и применяя оптимизацию компиляции (достаточно уровня О1). P.S. Чтобы статическая функция включалась всегда, нужно ее объявлять с аттрибутом __attribute__ ((used))

Ответ 4



Вероятно повысить уровень оптимизации в той среде разработки, в которой пишете. Сталкивался--помогало. Пробуйте =)

Ответ 5



Вы не путаетет компиляцию и линковку? На этапе компиляции обычно ничего не выбрасывается. А вот линковщик уже смотрим, что есть, а чего нет и просто не включает в результирующий файл. (Ну разве что включены всевозможные режимы для отладки, типа -ggdb). Что бы убедиться, что результат такой, как нужно, рекомендую смотреть дизассемблером и генерировать карту функций. Для этого добавьте такой параметр -Wl,-Map=a.map к gcc. Формат файла a.map не хитрый, и может быть понят. Там будут перечислены все функции, их адреса, размеры и другая интересная информация, которая позволит Вам найти, где можно ещё немного соптимизировать.

Ответ 6



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

среда, 5 февраля 2020 г.

Почему IDE требует С++ компилятор для С кода?

#c #ide #gcc #cpp



Code::Blocks 8.02
Ubuntu 10.04 LTS (x86_64)

Пишу Сишный код. Компилятор ругается, что нет g++, и просит установить. Установил.
Заработало. Теперь вопрос, в чем отличие скомпилированного кода Си в gcc от кода Си
в g++?    


Ответы

Ответ 1



Почему IDE требует С++ компилятор для С кода? Все очень просто. В качестве линкера (компоновщика связей) используется g++: gcc -Wall -O2 -c /home/gaal/TEST/TEST/main.c -o obj/Release/main.o g++ -o bin/Release/TEST obj/Release/main.o -s Теперь вопрос, в чем отличие скомпилированного кода Си в gcc от кода Си в g++? См. комментарии к предыдущему ответу. Если компилятор — gcc, а линкер — g++, то просто будут добавлены зависимости от библиотек C++. Если компилятор и линкер — g++ с ключами для C-кода, то опять же будут определенные библиотеки. Но по идее код будет «Сишным».

Ответ 2



В бинарник будет зашит рантайм C++, возможно, добавятся какие-нибудь дополнительные секции. Будут другие библиотечные зависимости: C: linux-vdso.so.1 => (0x00007fff6ab46000) libc.so.6 => /lib/libc.so.6 (0x00007f0eb1149000) /lib64/ld-linux-x86-64.so.2 (0x00007f0eb14f4000) C++: linux-vdso.so.1 => (0x00007fff12fff000) libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007fe1cab00000) libm.so.6 => /lib/libm.so.6 (0x00007fe1ca87d000) libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007fe1ca666000) libc.so.6 => /lib/libc.so.6 (0x00007fe1ca2e3000) /lib64/ld-linux-x86-64.so.2 (0x00007fe1cae2e000) Вот такой код: int main(){} скомпилированный (gcc, g++) и дизассемблированный отличался только строками: // в начале main .cfi_personality 0x3,__gxx_personality_v0 ... // при выходе из main movl $0, %eax В варианте C++ обе были добавлены. Первая строка - видимо, какие-то метаданные, а вот вторую объяснить не могу, но в общем ничего существенного. Зачем-то обнуляется регистр eax.

среда, 29 января 2020 г.

C++ - передача массива в функцию

#cpp #массивы #функции #gcc


Есть несколько функций, которые принимают в качестве параметра массив целых чисел.

#include 

using namespace std;

// нахождение наибольшего целого числа в массиве        
int FindLargest(int arr[])
{
    int largest = arr[0];
    for (int i = 1; i < arr.size(); i++)
    {
        if ( arr[i] > largest)
        {
            largest = arr[i];
        }
    }

    return largest;
}

// содержатся ли в массиве повторяющиеся элементы
bool ContainsDuplicates(int arr[])
{
    for (int i = 0; i < arr.size(); i++)
    {
        for(int j = 0; j < arr.size(); j++)
        {
            if (i != j)
            {
                if (arr[i] == arr[j]) return true;
            }
        }
    }

    return false;
}

// содержатся ли в массиве повторяющиеся элементы (ver 2.0)
bool ContainsDuplicates2(int arr[])
{
    for (int i = 0; i < (arr.size() - 1); i++)
    {
        for (int j = i + 1; j < arr.size(); j++)
        {
            if (arr[i] == arr[j]) return true;
        }
    }

    return false;
}

int main()
{

}


Компилятор выводит следующие ошибки. Не могу понять в чем они заключаются.

C:\Users\saibo>gcc algorithms.cpp
algorithms.cpp: In function 'int FindLargest(int*)':
algorithms.cpp:21:26: error: request for member 'max_size' in 'arr', which is of
non-class type 'int*'
  for (int i = 1; i < arr.size(); i++)
                          ^~~~
algorithms.cpp: In function 'bool ContainsDuplicates(int*)':
algorithms.cpp:34:26: error: request for member 'max_size' in 'arr', which is of
non-class type 'int*'
  for (int i = 0; i < arr.size(); i++)
                          ^~~~
algorithms.cpp:36:26: error: request for member 'max_size' in 'arr', which is of
non-class type 'int*'
   for(int j = 0; j < arr.size(); j++)
                          ^~~~
algorithms.cpp: In function 'bool ContainsDuplicates2(int*)':
algorithms.cpp:50:27: error: request for member 'max_size' in 'arr', which is of
non-class type 'int*'
  for (int i = 0; i < (arr.size() - 1); i++)
                           ^~~~
algorithms.cpp:52:31: error: request for member 'max_size' in 'arr', which is of
non-class type 'int*'
   for (int j = i + 1; j < arr.size(); j++)
                               ^~~~


Может пишу что-то не так. Буду рад подсказке!
    


Ответы

Ответ 1



arr в int FindLargest(int arr[]) - это не массив, а указатель. (При попытке создать параметр-массив, он автоматически превращается в указатель.) Указатель не знает размер массива, на который указывает. Вам нужно передавать размер массива как отдельный параметр. (Скажем, int FindLargest(int arr[], int size).) И вообще, . может применяться только к классам. Ни массивы, ни указатели не являются классами, так что .size() никак не мог сработать. Или, можно вообще отказаться от массивов и использовать std::vector. Вот на них .size() работает.

Ответ 2



Это вам не Java какая :), это С++ - при передаче массива надо передавать размер отдельно. Или пользоваться классами C++ типа vector, array...

вторник, 28 января 2020 г.

typedef с рекурсией в си

#c #gcc #clang


Коллеги, подскажите pls, можно ли в компиляторах gcc, clang объявить содержащий рекурсию тип?

Например, без рекурсии чудесно проходит, а с ней - нет:

struct {
    int a;
    char *b;
    my_t *next;
} typedef my_t;


Заранее признателен за подсказку.
    


Ответы

Ответ 1



Либо typedef struct my_t { int a; char *b; struct my_t *next; } my_t; либо typedef struct my_t my_t; struct my_t { int a; char *b; my_t *next; // или `struct my_t *next;` }; либо struct my_t { int a; char *b; struct my_t *next; }; typedef struct my_t my_t; Ключевое слово typedef можно порекомендовать ставить первым в typedef-объявлении, а не запихивать его в середину. Другими словами проблема "рекурсивного определения" решается путем назначения struct-типу непустого тэга и использования полного имени типа struct при объявлении внутреннего указателя struct my_t { int a; char *b; struct my_t *next; }; а куда при этом вы поставите ваш typedef struct my_t my_t; (до объявления струкутры, после объявления струкутры, сольете в одно объявление или вообще не будете делать typedef) - это уже вопрос посторонний.

Ответ 2



struct myt; typedef struct myt my_t; struct myt { int a; char *b; my_t *next; };

Как указать компилятору заполнить статический массив одинаковыми ненулевыми значениями?

#cpp #gcc #x86_64


Елементарный способ: произвести заполнение массива при инициализации. Но массив статический
и уже занимает место в образе. Нужно каким-то образом указать компилятору заполнить
область массива конкретным значением. 
Код static int arr[5]={1} заполняется как {1, 0, 0, 0, 0}, а нужно {1, 1, 1, 1, 1}

__attribute__((fillupper(1))) игнорируется gcc.

Массив неконстантный. Массив с 5 элементами - пример. Нужно заполнить массив в 4096
элементов.
Использование возможностей STL сильно ограничены, включая производные. Они все имеют
элементарное решение, указанное в первом предложении.

Доработка ответа:

template 
struct IArray {
  type val = v;
  IArray& operator=(type n){
    val=n;
    return *this;
  }
  IArray& operator|=(type n){
    val|=n;
    return *this;
  }
};


И необходимо дописать/изменить операторы для нужных Вам преобразований.
    


Ответы

Ответ 1



#include template struct Int { int val = v; }; int main() { Int<10> arr[1024]; std::cout << arr[10].val << std::endl; } https://godbolt.org/z/6-BEPi

Ответ 2



Не бейте меня. Нашёл только asm способ. //> g++ -std=c++11 memset.cpp #include asm( " .globl s2\n" " .data\n" " .align 32\n" " .type s2, @object\n" " .size s2, 16384\n" "s2: .fill 4096,4,777\n" ); extern "C" { extern int s2[4096]; }; int main(){ for(size_t j = 4096+1;j>0;){--j; std::cout<<"s2["<

Ответ 4



dd if=/dev/zero of=/tmp/sarr conv=swab count=4096 xxd -i /tmp/sarr | sed -e 's/0x00/0x01/g' > /tmp/parr.h P.S. :) UPD: #include template class ct_array { public: constexpr explicit ct_array(); public: const T& operator[](size_t idx) const; protected: T m_data[N]; }; template constexpr ct_array::ct_array() : m_data() { for (T *el = m_data; el != m_data + N; ++el) *el = def_val; } template const T& ct_array::operator[](size_t idx) const { return m_data[idx]; } template constexpr decltype(auto) make_ct_array() { return ct_array(); } int main() { constexpr auto arr = make_ct_array(); std::cout << arr[10] << std::endl; return 0; } UPD2: #include template class ct_array { public: constexpr explicit ct_array(T def); public: const T& operator[](size_t idx) const; protected: T m_data[N]; }; template constexpr ct_array::ct_array(T def) : m_data() { for (T *el = m_data; el != m_data + N; ++el) *el = def; } template const T& ct_array::operator[](size_t idx) const { return m_data[idx]; } template constexpr decltype(auto) make_ct_array(T def) { return ct_array(def); } int main() { constexpr auto arr = make_ct_array(3.14); std::cout << arr[10] << std::endl; return 0; }

Ответ 5



Можно написать функцию template void f(T a[], size_t s, const T& n) { for (size_t i = 0; i < s; ++i) a[i] = n; } Тогда const int n = 4096; int arr[n]; f(arr, n, 1); после ее вызова вы получите желаемый результат Или же воспользоваться стандартным альгоритмом const int n = 4096; int arr[n]; std::fill(arr, arr + n, 1);

Почему ОТКРЫТАЯ функция из родительского класса становится недоступной в ОТКРЫТО унаследованном классе

#cpp #qt #gcc #компилятор #стандарт


если в производном классе имеется функция с тем же именем, но различной сигнатурой?

Пример.
Создаем класс TwoStageMap, открыто унаследованного от QMap:

template>
class TwoStageMap : public QMap
{
public:
    void insert(const F &fkey, const S &skey, const T &value1); //TODO: return an
iterator of what?

    void insert(const QPair &pair, const T &value1);

    T value(const F &fkey, const S &skey);

    T value(const QPair &pair);

};


При попытки обратиться к value() с сигнатурой из производного класса (TwoStageMap)
все прекрасно работает:

TwoStageMap> tsMap;

tsMap.insert(31, "October", "Halloween");
tsMap.insert(31, "December", "New Year's Eve");
tsMap.insert(25, "December", "Xmas");
tsMap.insert({25, "October"}, "Canna");

qDebug() << "Day1:" << tsMap.value1(31, "December");


, но как только попытаемся вызвать value() самого QMap (чтобы получить внутренний
контейнер под первым ключем), tsMap.value(31);, то тут же получаем ошибку:


  no matching member function for call to 'value'


При переименовании функции, например, в value1() проблема исчезает.
Вопрос - это ошибка компилятора или стандарт языка?
Компирятор gcc x86 64bit



Реализация класса, если кто хочет его использовать/попробовать:

template
void TwoStageMap::insert(const F &fkey, const S &skey, const T &value)
{
    Cont innerMap;
        if (QMap::contains(fkey)) {
            innerMap = QMap::value(fkey);
        }

    innerMap.insert(skey, value);
    QMap::insert(fkey, innerMap);

    //TODO: return an iterator of what?
}

template
void TwoStageMap::insert(const QPair &pair, const T &value)
{
    return insert(pair.first, pair.second, value);
}

template
T TwoStageMap::value(const F &fkey, const S &skey)
{
    auto innerMap = QMap::value(fkey);
    return innerMap.value(skey);
}

template
T TwoStageMap::value(const QPair &pair)
{
    return value1(pair.first, pair.second);
}




PS 
В примере Вызов функции_члена шаблонного базового класса из функции производного
шаблонного класса ситуация с невидимостью неквалифицированного имени функции шаблонного
родительского класса, с этим, как раз, вопросов нет, у меня, как можно видеть, идет
обращение через QMap::, здесь же немного другой случай, а именно, вопрос в
том, что мешает компилятору распознать перегружанную функцию с другой сигнатурой, используя
т.н. "искажение имен"? Если это стандарт языка, то вопрос, скорее к Комитету
    


Ответы

Ответ 1



При вызове метода класса по имени без квалификатора tsMap.value(31); для построения списка перегрузок будет осуществлен поиск имени без квалификатора в области видимости класса TwoStageMap согласно спецификации обращения к членам класса: 6.4.5 Class member access [basic.lookup.classref] 2 If the id-expression in a class member access (8.5.1.5) is an unqualified-id, and the type of the object expression is of a class type C , the unqualified-id is looked up in the scope of class C. Этот поиск будет завершен при нахождении имени value в классе TwoStageMap, так как поиск имени без квалификатора должен завершаться сразу при нахождении первого объявления, согласно спецификации поиска имени без квалификатора: 6.4.1 Unqualified name lookup [basic.lookup.unqual] 1 In all the cases listed in 6.4.1, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. Таким образом метод value из базового класса QMap в списке перегрузок будет отсутствовать. Чтобы это исправить можно Внести этот метод в область видимости класса TwoStageMap добавив using QMap::value; При вызове использовать имя с квалификатором: tsMap.QMap::value(31);

Ответ 2



Если я правильно понял проблему, то решается эта проблема с помощью using. #include #include class Base { public: void func(int _v) { std::cout << __FUNCTION__ << std::endl; } }; class Derived : public Base { public: using Base::func; void func(const std::string &_str) { std::cout << __FUNCTION__ << std::endl; } }; int main(int argc, char *argv[]) { Derived d; d.func("text"); d.func(1); return 0; }

Ответ 3



Не важно какой класс вы напишете. Допустим вы написали такой простой класс: class Your_class { protected: int n{ 3 }; public: int value(int n) const { return n + 3; } }; Если я наследую ваш класс, то я наследую все, что не является закрытым членами вашего класса: class My_class : public Your_class { }; В таком виде мой класс имеет функцию_член и обьект n вашего класса. Но как только я добавлю в класс: class My_class : public Your_class { public: bool value(const std::string& s) { return n == s.length(); } }; Этим я выражаю, что моя value это совсем другая функция(мне не нужен ваш вариант). И экземпляры моего класса не будут иметь возможность пользоваться одноименной функцией базового вашего класса. Для обеспечения этой возможности я смогу написать другую функцию, которая вызовит value вашего класса, или же сделать так, как описан в другом ответе. Точно также, если я добавляю в класс свой обьект n, то экземпляры моего класса будут пользоваться только этим обьектом. Так что, функция=член в производном классе с таким же именем, что и функция_ член в базовом, не является ее перегрузкой, а является ее заменой

пятница, 24 января 2020 г.

Как вывести русский текст в консоль

#cpp #c #visual_cpp #gcc #mingw


Есть UTF-8 строка с русским текстом: "\xd1\x82\xd0\xb5\xd0\xba\xd1\x81\xd1\x82".

Надо вывести ее на консоль так, чтобы это работало на следующих трех платформах и
компиляторах:


Windows и VC++,
Windows и MinGW,
Linux и gcc.


Предполагается что шрифт консоли позволяет показать хотя бы русские буквы.

Следует обратить внимание на то, что в MinGW локали реализованы не полностью, и многие
решения для VC++ не будут работать.
MinGW можно взять здесь.

Можно использовать #ifdef. Можно использовать сторонние библиотеки, например Boost.



Вот пример решения дня MinGW, но нужна поддержка всех трех платформ.

> type main.cpp
#include 
#include 
#include 
#include 

int main() {
    std::wstring_convert,
wchar_t> conv;
    std::wstring ws = conv.from_bytes("[test \xd1\x82\xd0\xb5\xd1\x81\xd1\x82 \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88]\n");
    DWORD _; ::WriteConsoleW(::GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), ws.size(),
&_, nullptr);
}

> g++ -std=c++14 -O2 -Wall -pedantic main.cpp && a
[test тест テスト]

    


Ответы

Ответ 1



Библиотека Qt (достаточно Core) кроссплатформенная и собирается любым компилятором. Вариант вывода в консоль: QString string = QString::fromUtf8("Привет мир !"); // для консоли Windows QTextCodec *codec = QTextCodec::codecForName("IBM 866"); // для Linux QTextCodec *codec = QTextCodec::codecForName("System"); QByteArray encodedString = codec->fromUnicode(string); std::cout << encodedString.data(); Список поддерживаемых Qt кодеков