LINUX.ORG.RU

Вышел драйвер для GDI-принтеров Canon

 , , , ,


0

3

Первая работоспособная (хотя и не очень) версия открытого драйвера для принтеров Canon CAPT (LBP-****) вышла сегодня. В отличие от проприетарного драйвера, открытый является легковесным, не требует запуска демонов при загрузке системы и не саботирует работу принтеров других производителей.

При установке драйвера обратите внимание на выбор правильного устройства из списка (не usb://, а обязательно capt://) и на правильность файла *.ppd (в нем определены низкоуровневые параметры принтера, и с неправильным *.ppd принтер будет, скорее всего, печатать полосы).

Изменения:

  • полностью переписан код драйвера (с C на C++)
  • теперь драйвер работает через libusb и является бакэндом для CUPS
  • автоопределение принтеров
  • в основном устранены зависания принтера при сбоях печати

Недоработки:

  • не проверяется наличие бумаги!
  • при печати более чем одной страницы может печататься только первая
  • при печати очень сложных страниц иногда переполняется память принтера, «хвост» страницы обрезается
  • неправильные верхние и нижние поля

На сегодняшний день поддержан и проверен LBP-2900 и, вероятно, будет работать очень похожий на него LBP-3000. Другие модели не проверялись, так как их нет у разработчика.

ПРОЕКТУ ДЛЯ РАЗВИТИЯ ТРЕБУЮТСЯ РАЗРАБОТЧИКИ!

Требования: знание C++, наличие одного из CAPT-принтеров.

>>> Скачать

Ответ на: комментарий от anonymous

> Хомячий код - это не когда смотреть страшно, а когда человек берется писать драйвер устройства и, вместо того чтобы осилить правильную расстановку inline, тупо переписывает все на С++.

Анонимус, покажи, как пишешь ты. Как пишу я, все уже видели.

Про inline и ее смысл читай выше. Не знаешь, что inline делает - молчи в тряпочку.

Yampp ()
Ответ на: комментарий от Yampp

Для корректной работы обработки ошибок нужны автоматические деструкторы. Иначе будете в каждой строчке if(error) free(memory) писать. И допишетесь до утечки памяти в лучшем случае.

Подождите. Вы же сами пишете, что там нет «тормознутой фичи» new. Откуда же там может взяться free(memory)? :)

Стиль кодирования, когда в каждой строчке пишется if(error) free(memory), оставляю без комментариев.

Откройте стандарт на язык Си и посмотрите, что на самом деле делает слово inline и почему оно не дает такого эффекта в языке, в котором нет ссылочных типов данных.

Для начала, зачем вам ссылочные типы данных? Впрочем, знаю. Без них людям, «думающим на С++», драйвер устройства не написать :) Ну, и было бы интересно, где в стандарте на язык Си говорится об эффекте слова inline на ссылочные типы данных.

anonymous ()
Ответ на: комментарий от Yampp

> Откройте стандарт на язык Си и посмотрите, что на самом деле делает слово inline

Вот выдержка из раздела 6.7.4:

" Semantics
5 A function declared with an inline function specifier is an inline function. The function specifier may appear more than once; the behavior is the same as if it appeared only once. Making a function an inline function suggests that calls to the function be as fast as possible.123) The extent to which such suggestions are effective is implementation-defined.124)
................
123) By using, for example, an alternative to the usual function call mechanism, such as ‘‘inline substitution’’. Inline substitution is not textual substitution, nor does it create a new function...."

Тебя так испугало «implementation defined»? Так это зря. Нормальные компиляторы делают именно «inline substitution».

почему оно не дает такого эффекта в языке, в котором нет ссылочных типов данных


И почему? Не затруднит пояснить?

fang ()
Ответ на: комментарий от Yampp

Анонимус, покажи, как пишешь ты.

То есть, если ты увидишь, что мой код плохой, то твой код от этого станет лучше? :)

Как пишу я, все уже видели.

Так это ты автор этого драйвера? Тогда понятно, почему «хомячки на C++ так писать не могут» :)

Про inline и ее смысл читай выше. Не знаешь, что inline делает - молчи в тряпочку.

Да не обижайся ты :) Собственно к твоему коду у меня претензий нет. Просто, создается впечатление, что ты хорошо знаешь С++, а Си - не очень. И пишешь не на чем принято/удобнее писать в данной ситуации, а на чем тебе удобнее. Понимаешь, можно виртуозно забивать гвозди с помощью лопаты, но со стороны это выглядит несколько комично, потому гвозди забивают обычно по-другому :)

anonymous ()
Ответ на: комментарий от anonymous

> Подождите. Вы же сами пишете, что там нет «тормознутой фичи» new. Откуда же там может взяться free(memory)?

Ну не free, ну close, или undo_some_operation, или еще что-нибудь в этом роде. Конкретно в коде драйвера около двух десятков таких вещей, которые при ошибке надо корректно откатить в строго обратном порядке (деинициализация драйвера USB). Любые ошибки чреваты утечкой ресурсов. В таких языках, как Си, автоматика генерации кода обработки ошибок не предусмотрена. В C++ она есть за счет автоматических деструкторов.

Для начала, зачем вам ссылочные типы данных? Впрочем, знаю.

Нет, не знаете. Вот простой пример кода:

struct S { int a; int b; };

struct S x;

inline void f(const struct S* p) { чего-то там }

и далее по коду: f(&x);

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

Тот же код на C++:

inline void f(const S& x) { чего-то там } f(x);

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

Это вкупе с некоторыми другими особенностями семантики языка приводит к тому, что на Си вызывать инлайновую функцию, скажем, для каждого байтика массива чревато (будут делаться вспомогательные операции) и надо писать явный цикл одного уровня, а на C++ такая операция совершенно бесплатна и к генерации лишнего кода не приводит.

Есть на самом деле еще много подобных вещей, просто их перечислять долго. Это все из более чем 10-летней практики реальной работы с C, C++ и многими другими языками. Различия языков на первый взгляд незначительные, но код генерируется совершенно по разным принципам.

Yampp ()
Ответ на: комментарий от fang

The extent to which such suggestions are effective is implementation-defined.121) ... 121) For example, an implementation might never perform inline substitution, or might only perform inline substitutions to calls in the scope of an inline declaration.

Но, с другой стороны, если функцию объявить как static inline, то при включенной оптимизации она будет всегда нилиниаризироваться, а если ее объявить с атрибутом always_inline, то вообще всегда. И зачем пытаться достигнуть того же эффекта путем переписывания всего на С++ - мне не понятно.

http://gcc.gnu.org/onlinedocs/gcc/Inline.html

Кстати, было бы интересно, что говорит по поводу inline стандарт на С++? Почти уверен, что там он тоже implementation-defined.

anonymous ()

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

anonymous ()
Ответ на: комментарий от Yampp

Вот простой пример кода: ...

Хорошо. Попробуем.

#include <stdio.h>

struct S { int a; int b; };

struct S x;

static inline void f(const struct S* p) {

	printf("%d\n", p->a);
}

int main(){

	f(&x);
	
	return 0;
}
gcc -O2 -c test.c
.globl		_main
.extern		__alloca
.extern		___main
.extern		_x
.extern		_printf
.new_section .text, "crx4"
_main:
    push        ebp 
    mov         eax,0x00000010 
    mov         ebp,esp 
    sub         esp,0x00000008 
    and         esp,0xfffffff0 
    call        near ptr j^__alloca 
    call        near ptr j^___main 
    mov         dword ptr [esp],.rdata 
    mov         eax,dword ptr _x 
    mov         dword ptr 0x4[esp],eax 
    call        near ptr j^_printf 
    leave       
    xor         eax,eax 
    ret         
    nop         
    nop         
    nop         
    nop         
    nop         
    nop         
    nop         
    nop         
    nop         
    nop         
    nop         
    nop         
    nop         
    nop         
    nop         
.new_section .bss, "urw2"
.bss:

.new_section .rdata, "dr2"
    .asciiz	"%d\n"
anonymous ()
Ответ на: комментарий от Yampp

Нет, не знаете. Вот простой пример кода:

struct S { int a; int b; };

struct S x;

inline void f(const struct S* p) { чего-то там }

и далее по коду: f(&x);

Блаблабла

Ну и смотри. test_inline.c:

#include <stdio.h>
#include <stdlib.h>

struct S {
  int x;
  int y;
};

static inline int add_or_sub(struct S *s)
{
    if (s->y < s->x) {
        return s->y + s->x;
    }
    else {
        return s->y - s->x;
    }
}

int main(void)
{
    struct S s;
    int y;

    s.y = random();
    s.x = random();

    y = add_or_sub(&s);

    printf("%d\n", y);
}

xgromd@xgromd-desktop:~/test$ gcc -S test_inline.c -O2

xgromd@xgromd-desktop:~/test$ cat ./test_inline.s

        .file   "test_inline.c"
        .section        .rodata.str1.1,"aMS",@progbits,1
.LC0:
        .string "%d\n"
        .text
        .p2align 4,,15
.globl main
        .type   main, @function
main:
.LFB19:
        .cfi_startproc
        pushq   %rbx
        .cfi_def_cfa_offset 16
        .cfi_offset 3, -16
        call    random
        movl    %eax, %ebx
        call    random
        movl    %ebx, %edx
        leal    (%rax,%rbx), %esi
        movl    $.LC0, %edi
        subl    %eax, %edx
        cmpl    %eax, %ebx
        popq    %rbx
        cmovge  %edx, %esi
        xorl    %eax, %eax
        jmp     printf
        .cfi_endproc
.LFE19:
        .size   main, .-main
        .ident  "GCC: (Debian 4.4.5-6) 4.4.5"
        .section        .note.GNU-stack,"",@progbits

fang ()
Ответ на: комментарий от anonymous

Дальше.

#include <iostream>

using namespace std;

struct S { int a; int b; };

struct S x;

inline void f(const struct S& p) {

	cout << p.a << endl;
}

int main(){

	f(x);
	
	return 0;
}

gcc -O2 -c test.cpp
.globl		_main
.globl		_x
.extern		__alloca
.extern		___main
.extern		__ZSt4cout
.extern		__ZNSolsEm
.extern		__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
.extern		__ZNSolsEl
.extern		__ZNSt8ios_base4InitC1Ev
.extern		__ZNSt8ios_base4InitD1Ev
.new_section .text, "crx4"
_main:
    push        ebp 
    mov         eax,0x00000010 
    mov         ebp,esp 
    sub         esp,0x00000008 
    and         esp,0xfffffff0 
    call        near ptr j^__alloca 
    call        near ptr j^___main 
    mov         eax,dword ptr __ZSt4cout 
    mov         edx,dword ptr .bss 
    mov         eax,dword ptr -0xc[eax] 
    add         eax,__ZSt4cout 
    mov         eax,dword ptr 0xc[eax] 
    test        al,0x40 
    jne         X$1 
    test        al,0x08 
    je          X$2 
X$1:
    mov         dword ptr 0x4[esp],edx 
    mov         dword ptr [esp],__ZSt4cout 
    call        near ptr j^__ZNSolsEm 
    mov         dword ptr [esp],eax 
    call        near ptr j^__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ 
    leave       
    xor         eax,eax 
    ret         
X$2:
    mov         dword ptr 0x4[esp],edx 
    mov         dword ptr [esp],__ZSt4cout 
    call        near ptr j^__ZNSolsEl 
    mov         dword ptr [esp],eax 
    call        near ptr j^__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ 
    leave       
    xor         eax,eax 
    ret         
    nop         
__Z41__static_initialization_and_destruction_0ii:
    push        ebp 
    mov         ebp,esp 
    sub         esp,0x00000018 
    mov         dword ptr -0x8[ebp],ebx 
    cmp         edx,0x0000ffff 
    sete        bl 
    mov         dword ptr -0x4[ebp],esi 
    cmp         eax,0x00000001 
    mov         esi,eax 
    sete        al 
    test        bl,al 
    jne         X$4 
    test        esi,esi 
    sete        al 
    test        bl,al 
    jne         X$5 
X$3:
    mov         ebx,dword ptr -0x8[ebp] 
    mov         esi,dword ptr -0x4[ebp] 
    mov         esp,ebp 
    pop         ebp 
    ret         
X$4:
    mov         dword ptr [esp],.bss+0x8 
    call        near ptr j^__ZNSt8ios_base4InitC1Ev 
    test        esi,esi 
    sete        al 
    test        bl,al 
    je          X$3 
    lea         esi,[esi] 
X$5:
    mov         dword ptr [esp],.bss+0x8 
    call        near ptr j^__ZNSt8ios_base4InitD1Ev 
    mov         ebx,dword ptr -0x8[ebp] 
    mov         esi,dword ptr -0x4[ebp] 
    mov         esp,ebp 
    pop         ebp 
    ret         
    lea         esi,[esi] 
    lea         edi,[edi] 
__GLOBAL__I_x:
    push        ebp 
    mov         edx,0x0000ffff 
    mov         ebp,esp 
    pop         ebp 
    mov         eax,0x00000001 
    jmp         __Z41__static_initialization_and_destruction_0ii 
__GLOBAL__D_x:
    push        ebp 
    mov         edx,0x0000ffff 
    mov         ebp,esp 
    pop         ebp 
    xor         eax,eax 
    jmp         near ptr __Z41__static_initialization_and_destruction_0ii 
.new_section .bss, "urw3"
_x:
    .lcomm	.bss, 0x00000008
    .lcomm	__ZSt8__ioinit, 0x00000010

.new_section .ctors, "drw2"
    .long	.text+0xe0

.new_section .dtors, "drw2"
    .long	.text+0xf0

Как видим, сгенерированный код практически идентичен, за исключением того, что С++ добавил туда еще кучу своего мусора.

anonymous ()
Ответ на: комментарий от anonymous

> Короче, в конце концов, по дистрам распихают ещё один не доделанный драйвер.То есть ещё один повод для хомячков ужаснуться и поставить обратно винду.

Напиши драйвер получше?

yytreop ()
Ответ на: комментарий от jeep

Хехе, народ не знает, что это такое - реверс-инженерить.

yytreop ()

А между тем автор прав.
Те кто пишет драйвера под Linux на C прекрасно знают целый подвал меток типо:
end_module2:

end_module1:

и т.д. с перемещением при эксепшене (даже не экзепшене а неверном значении контрольных переменных) с помощью GOTO в нужную метку.

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

Для такого высокоуровнего драйвера который работает в пространстве пользователя гораздо удобнее использовать именно C++. Даже Mesa начала к С++ приходить.

stalkerg ★★★★★ ()
Ответ на: комментарий от anonymous

> Как видим, сгенерированный код практически идентичен, за исключением того, что С++ добавил туда еще кучу своего мусора.

Для простых примеров это действительно так, особенно если не включать опцию -Os. Для сложных - нет. gcc действительно умеет раскручивать в инлайн даже вызовы функций по указателю, но семантически код все равно остается другим. Вообще, если говорить о программировании о написании быстрого надежного кода (а не просто быстрого и не просто надежного), то и Си и C++ для этого подходят плоховато, и выбор между ними зависит от конкретной задачи.

Первоначальная версия драйвера была написана на Си. Причина такого выбора: я считал, что драйвер - это просто реализация некоего алгоритма, который я напишу в императивном стиле, и выбрал Си как более подходящий для этого стиля язык. В процессе развития выяснилось, что логика работы принтера не такая, как казалось вначале, и ребром встал вопрос о многих частых масштабных переделках кода. А это уже ниша ООП с его инкапсуляцией. Поэтому код и был переделан на плюсы: на Си «сильно оптимизированный» и «легко переделываемый» - взаимоисключающие понятия, а на C++ их сочетать можно.

Кстати, что бы там ни говорили идеологи, cout - как раз одна из неудачных выдумок C++, могу объяснить, почему. Поэтому чаще всего ставят старый добрый printf.

Я не фанат того или иного языка, язык я выбираю в зависимости от требований к задаче. Я могу выбрать простоту Си, строгую типизацию и автоматику C++, быстрое написание Python, интеллектуальность Haskell, умение молотить строки Perl или распространенность PHP и т.д. в зависимости от того, что именно мне требуется написать. В данном конкретном случае я решил, что для данного конкретного кода мне нужны возможности, имеющиеся в C++ и отсутствующие в Си. А вот утилиты, которые я использовал в процессе реверс-инжениринга и отладки, напротив, полностью написаны на чистом Си: там мне нужна была прозрачность логики, и даже если бы я дал файлу расширение .cpp, внутри был бы по сути сишный код.

Yampp ()

Готов потестировать на LBP-810 Если нужно - пишите требьования к тестированию на gumanoed на gmail

gumanoed ★★★ ()
Ответ на: комментарий от IT-specialist

а я удивляюсь, как это еще не вспомнили про Gasoline Direct Injection (в простонародье «джидай»)

// бывший «счастливый» пользователь Canon LBP MF3200 //// счастливый пользователь hp 1536dnf mfp

v0rbis ★★ ()
Ответ на: комментарий от gumanoed

Для тестирования на LBP-810 берите версию из SVN. Как будут хоть какие-то результаты - выходите на связь по e-mail, обсудим.

Yampp ()
Ответ на: комментарий от stalkerg

> деструкторы у классов очень красиво реализуют механизмы последовательной инициализации и де инициализации устройств.

В точку. См. libusbxx.h, libusbxx.cpp в исходниках драйвера. Основное использование C++ именно там. И именно с этой целью.

Yampp ()
Ответ на: комментарий от stalkerg

А между тем автор прав. Те кто пишет драйвера под Linux на C прекрасно знают целый подвал меток типо: end_module2:

end_module1:

и т.д. с перемещением при эксепшене (даже не экзепшене а неверном значении контрольных переменных) с помощью GOTO в нужную метку.

Разница в том, что при использовании goto вы имеете полный контроль над вашим кодом, а при использовании эксепшенов вы не знаете что именно происходит на уровне машинного кода, когда генерится эксепшен. Этот процесс, по-моему, даже не стандартизирован. К тому же эксепшены раздувают код и в некоторых случаях могут его замедлять. Всё это имеет значение для таких низкоуровневых программ, как драйверы и т.п.

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

Очень спорное утверждение. По-моему, код

void fn() {
    obj = init(1);
    //...
    close(&obj);
}
лучше, чем
void fn() {
    MyObj obj(1);
    //...
}

Во втором случае С++ деинициализирует объект «под капотом» - вы не знаете как, в какой момент он это делает и не имеете над этим никакого контроля. К тому же, подобная практика приучает программиста не освобождать ресурсы, что, при отсутствии сборщика мусора в С++, чревато.

anonymous ()
Ответ на: комментарий от Yampp

Для простых примеров это действительно так, особенно если не включать опцию -Os. Для сложных - нет. gcc действительно умеет раскручивать в инлайн даже вызовы функций по указателю, но семантически код все равно остается другим. Вообще, если говорить о программировании о написании быстрого надежного кода (а не просто быстрого и не просто надежного), то и Си и C++ для этого подходят плоховато, и выбор между ними зависит от конкретной задачи.

Я привел факты, вы - нет. Поэтому я пока считаю ваше утверждение голословным. То, что С++ генерит якобы более быстрый код чем Си, я считаю несерьезным. Истинно скорее обратное.

Первоначальная версия драйвера была написана на Си. Причина такого выбора: я считал, что драйвер - это просто реализация некоего алгоритма, который я напишу в императивном стиле, и выбрал Си как более подходящий для этого стиля язык. В процессе развития выяснилось, что логика работы принтера не такая, как казалось вначале, и ребром встал вопрос о многих частых масштабных переделках кода. А это уже ниша ООП с его инкапсуляцией. Поэтому код и был переделан на плюсы: на Си «сильно оптимизированный» и «легко переделываемый» - взаимоисключающие понятия, а на C++ их сочетать можно.

Такое ощущение, что вы не отличаете язык программирования от технологии программирования. Инкапсуляцию очень просто сделать на Си, и оверхед будет меньше, чем у С++, потому что не надо тащить за собой таблицу виртуальных методов и всё, что С++ генерит «втихую».

anonymous ()
Ответ на: комментарий от Yampp

не реагируй на бездарных идиотов

не реагируй на бездарных идиотов, молодца - важное дело делаешь , ибо нет мочи с ccpd гадким на canon-ах сношаться :)

как от разраба толку от меня 0, но как тестер сгожусь тебе добрый молодец

me пошел делать PKGBUILD для foo2capt

oldbay ()
Ответ на: комментарий от anonymous

То, что С++ генерит якобы более быстрый код чем Си, я считаю несерьезным. Истинно скорее обратное.

Истина в том, что если все или почти все писать в *.h как inline, то C и C++ работают одинаково быстро. Но C++ имеет способы ограничения доступа к деталям реализации, а C не имеет. Поэтому на Си в подобных случаях не редкость такой код:

/*file.h:*/

struct str_t
{
    int a;
    int private_do_not_touch_x;
};

/*file.c:*/

#define private_do_not_touch_x x
#include "file.h"

Если это «понятный и читаемый стиль», то я Папа Римский. Гораздо чаще делают так:

/*file.h:*/

typedef struct Struct struct_t;
struct_t* work_begin(void);
void work_do(struct_t* s);
void work_end(struct_t* s);

/*file.c:*/

struct Struct
{
   int a;
};

struct_t* work_begin(void)
{
    struct_t* ret = (struct_t*) malloc(sizeof(struct_t));
    ...
    return ret;
}

Вот и приехали - плакал наш inline, плакало выделение на стеке. Пришлось ими пожертвовать ради надежности и читаемости кода.

потому что не надо тащить за собой таблицу виртуальных методов и всё, что С++ генерит «втихую».

C++ не генерирует таблиц виртуальных методов для классов, в которых не используется virtual.

Оверхед берется вот откуда. Инкапсуляция на Си делается примерно так:

struct stream1_t { ... };
void stream1_do(struct stream1_t* stream);

struct stream2_t { ... };
void stream2_do(struct stream1_t* stream);

stream2_t s;
stream2_do(&s);

Если здесь хочется сделать код, не зависящий от того, какой именно stream используется, то придется делать указатель на функцию или #define. Иначе:

void do()
{
    stream1_t s;
    stream1_do1(&s);
    stream1_do2(&s);
    stream1_do3(&s);
}

И как теперь это переделать на stream2, если приспичит?

Если писать на C++ «по-тупому», то в общем-то получается то же самое:

class IStream { public: virtual void do() = 0; ... };
class Stream1 : public IStream { ... };
class Stream2 : public IStream { ... };

IStream* s = ...;
s->do();

что хреново. И так писать не надо (точнее, это только для крайних случаев - когда надо на рантайме разбираться, что вызывать). А более опытные делают так:

class Stream1 { public: void do(); ... };
class Stream2 { public: void do(); ... };

что вообще ничем не отличается от Си, кроме способа записи (метод пишем не снаружи, а внутри скобок). Ничем - кроме одной мелочи. Вот такой:

void do()
{
    Stream1 s;
    s.do1();
    s.do2();
    s.do3();
}

Нетрудно проверить, что код будет сгенерирован тут точно такой же, как и в Си, но слово Stream1 упоминается только один раз, и проблем с его заменой не возникает. Ладно, а если у нас не одна такая функция, а две? Элементарно, Ватсон:

template <class S>
class Do
{
public:
    static inline void do1() { S s; s.do1(); }
    static inline void do2() { S s; s.do2(); }   
};

typedef Do<Stream1> DoIt;

DoIt::do1();
DoIt::do2();

Аналогом на Си является только #define. Или Find-Replace в текстовом редакторе.

Yampp ()
Ответ на: комментарий от Yampp

есть как lbp2900 , так и всякие lbp 3000-e тоже есть на конторе понакупили ... блин

oldbay ()
Ответ на: комментарий от Yampp

Тут вот какая штука. Очень важно, чтобы ширина картинки была не-помню-сколько пикселей (см. capt.drv), отличие хоть в один пиксель приводит к полосам. ... Подогнать битовые размеры можно, печатая белый лист, или же создавая файл с помощью cupsfilter и потом просматривая его raster-просмотрщиком.

Что именно нужно изменять в capt.drv? изменения HWMargins на +/- 1 для каждого значения не изменили ничего, размер растра по прежнему 4958x7017

как все-таки запускать cupsfilter руками?

что должно быть в в команде

cupsfilter -m application/ -p ppd/Canon-LBP-2900.ppd.gz file > raster

в -m application/ после слеша?

HighwayStar ★★★★★ ()

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

Canon та еще говноконтора... закопать бы ее

mCoRN ()
Ответ на: комментарий от Yampp

Но C++ имеет способы ограничения доступа к деталям реализации, а C не имеет.

Во-первых, имеет. Что мешает вам написать

struct {
    int a;
    void * priv;
};
и работать с полем priv внутри модуля как вам заблагорассудится? Во-вторых, попробуйте добавить/удалить любое private-поле в базовом классе, и вам придется перекомпилировать всю иерархию снизу доверху. Это - цена вашей инкапсуляции.

Если это «понятный и читаемый стиль», то я Папа Римский.

Я действительно не понял, что должен был показать этот код.

Вот и приехали - плакал наш inline, плакало выделение на стеке. Пришлось ими пожертвовать ради надежности и читаемости кода.

Во-первых, inline не «плакал», вам это только что продемонстрировали. Во-вторых, причем здесь выделение на стеке? Ваш код, насколько я понимаю, создает глобальный объект. Было бы странно создавать его на стеке.

И как теперь это переделать на stream2, если приспичит?

Такие вещи, вообще-то, продумываются заранее, а не переделываются если приспичит :)

/* common.h */

struct Class {
	void (*do1)(void * s);
	void (*do2)(void * s);
};

/* file1.h */

struct struct1{
	struct Class * class;
	int a;
};

/* file2.h */

struct struct2{
	struct Class * class;
	char b;
};


/* file1.c */


static void struct1_do1(void * s) {
	struct struct1 * str = (struct struct1*)s;

	str->a = 1;
}

static void struct1_do2(void * s) {
	struct struct1 * str = (struct struct1*)s;

	str->a = 2;
}

static struct Class struct1_class = { 
	struct1_do1, 
	struct1_do2
};

void init1(struct struct1 * s) {

	s->class = &struct1_class;
}

/* file2.c */

static void struct2_do1(void * s) {
	struct struct2 * str = (struct struct2*)s;

	str->b = 'x';
}

static void struct2_do2(void * s) {
	struct struct2 * str = (struct struct2*)s;

	str->b = 'y';
}

static struct Class struct2_class = { 
	struct2_do1, 
	struct2_do2
};

void init2(struct struct2 * s) {

	s->class = &struct2_class;
}

/* test.c */

void do1(void * s) {
	struct Class * cls = *((struct Class **)s);

	cls->do1(s);

}

void do2(void * s) {
	struct Class * cls = *((struct Class **)s);

	cls->do2(s);

}

struct struct1 s1;
struct struct2 s2;

int main(){

	init1(&s1);
	init2(&s2);

	do1(&s1);
	do1(&s2);
	do2(&s1);
	do2(&s2);

	
	return 0;
}

anonymous ()
Ответ на: комментарий от HighwayStar

cupsfilter -m application/vnd.cups-raster -p ppd/Canon-LBP-2900.ppd.gz file > raster

Попробуй версию из SVN (и ppd из SVN), там я вроде как жесткий размер строки сделал. Видимо, изменилась работа фильтра, он теперь не хочет управляться полями из PPD. В документации на CUPS - ни слова.

Yampp ()
Ответ на: комментарий от anonymous

Что мешает вам написать ... и работать с полем priv внутри модуля как вам заблагорассудится?

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

Ваш код, насколько я понимаю, создает глобальный объект. Было бы странно создавать его на стеке.

Вот типичное использование таких объектов:

for (size_t i = 0; i < sizeof(some_large_array); ++i)
{
    Actor a(something, i);
    a.work(data[i]);
}
Догадываетесь, что будет, если переписать это так:
for (size_t i = 0; i < sizeof(some_large_array); ++i)
    actor_t* a = actor_new(something, i);
    actor_work(a, data[i]);
    actor_free(a);
}
?

Создать actor_t на стеке предлагаете? Но тогда интерфейс структуры светится в общую область видимости, и через два дня я обнаружу, что код перестал работать, потому что какой-то умник добавил туда что-то вроде a.secret_variable = 42; с какого-то гондураса. Нет, спасибо, не надо такого.

Вообще-то даже запись a->b уже потенциально тормозная, так как имеет шанс скомпилироваться в косвенное обращение, лишь на оптимизатор вся надежда. А в программе, состоящей более из одного юнита компиляции, это почти гарантировано. C++ позволяет решить проблему за счет вынесения столь критичных мест прямо в *.h - да, при изменении приватных полей при этом перекомпилируется весь мир, но это справедливая цена за скорость работы кода. В Си писать что-то в *.h очень плохо из-за областей видимости, а в C++ уже нестрашно.

Такие вещи, вообще-то, продумываются заранее, а не переделываются если приспичит :)

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

Yampp ()
Ответ на: комментарий от anonymous

Покажите мне, тупому, как написать эквивалентный код на Си, не теряя эффективности (сохранить инлайн и размещение m_rand в регистре) и не вынося m_rand в публичную видимость. Учтите, что инстанция класса Rand не единственная, а создаваться на стеке он может многие тысячи раз в цикле.

class Rand
{
public:
    Rand() : m_rand(::rand()) { }
    inline int get() const { return m_rand * 42; }
private:
    const int m_rand;
};

Обратите внимание, что копирование класса Rand не запрещено, запись

Rand a;
Rand b(a);
допустима.

Yampp ()
Ответ на: комментарий от Yampp

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

Подождите. Во-первых, почему вы решили, что для поля priv обязательно должен вызываться malloc и что эта структура обязательно должна создаваться-уничтожаться, да еще в критичных по скорости местах? Мы говорили о том, может ли Си скрывать реализацию, не?

Создать actor_t на стеке предлагаете? Но тогда интерфейс структуры светится в общую область видимости, и через два дня я обнаружу, что код перестал работать, потому что какой-то умник добавил туда что-то вроде a.secret_variable = 42; с какого-то гондураса. Нет, спасибо, не надо такого.

Так С++ этому подвержен точно так же! Чтобы написать Actor a(something, i) нужно точно так же засветить описание класса в общую область видимости и точно так же кто-нибудь может добавить в любой раздел этого описания всё что угодно! В этом отношении priv даже безопаснее, т.к. он заменяет целый раздел private, выставленный на всеобщее обозрение.

anonymous ()
Ответ на: комментарий от Yampp

Покажите мне, тупому, как написать эквивалентный код на Си, не теряя эффективности (сохранить инлайн и размещение m_rand в регистре) и не вынося m_rand в публичную видимость. Учтите, что инстанция класса Rand не единственная, а создаваться на стеке он может многие тысячи раз в цикле.

Объясните сначала, зачем этот код вообще нужен и почему нельзя просто написать

rand() * 42
?

anonymous ()

Спасибо. Желаю удачи. Могу пожертвовать Canon LBP-810, работающий через COM. Но насколько я понимаю, нужен USB принтер.

anonymous ()
Ответ на: комментарий от anonymous

Подождите. Во-первых, почему вы решили, что для поля priv обязательно должен вызываться malloc и что эта структура обязательно должна создаваться-уничтожаться, да еще в критичных по скорости местах? Мы говорили о том, может ли Си скрывать реализацию, не?

Вот и получается: скрывать может, но не всегда бесплатно. Если структура статическая, так я ее вообще в ашку выносить не буду ни в Си, ни в плюсах. Проблема возникает, если инстанций много. Тут вариантов всего два - либо private, либо непрозрачный указатель в ашке и маллок в сишке. Что эффективнее?

В этом отношении priv даже безопаснее, т.к. он заменяет целый раздел private, выставленный на всеобщее обозрение.

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

Объясните сначала, зачем этот код вообще нужен и почему нельзя просто написать rand() * 42 ?

Разумеется, затем, что в реальном примере там не rand() и не умножение, а более сложные операции. Но суть та же: есть какое-то маленькое поле, которое можно хранить в регистре; оно получается при инициализации (в данном случае ее имитирует rand) и никогда не показывается в «сыром» виде, а всегда в обработанном («умноженном на 42»). Вот более реалистичный пример (с купюрами; полностью см. в коде драйвера):

class Input
{
public:
    class Pos
    {
        friend class Input;
    private:
        Pos(size_t pos) : m_pos(pos) { }
        size_t m_pos;
    };
public:
    Input(...);
    uint8_t get_byte();
    bool empty() const;
    Pos save_pos() const { return Pos(pos); }
    void restore_pos(const Pos& pos) { m_pos = pos.m_pos; }
private:
    size_t m_pos;
};

То есть, можно сделать Input::Pos p = in.save_pos(); и можно сделать in.restore_pos(p); , но нельзя никаким образом узнать и/или изменить то, что в Pos хранится. Замечательная защита, и не столько от дурака, сколько от непредусмотренного кода: теперь я ТОЧНО знаю, что ЛЮБОЙ вызов restore_pos() делается СТРОГО с тем, что когда-то вернул какой-то другой вызов save_pos() и не может быть сделан с произвольным числом. Если бы я вместо Pos возвращал и принимал size_t, это дало бы возможность подставить любую лажу и потом долго искать ошибку. Сейчас круг поиска возможной лажи резко сужен - единственное, что можно спутать, это два разных Pos друг с другом. Обо всем остальном позаботится компилятор - откажется компилировать неверный код.

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

Yampp ()
Ответ на: комментарий от anonymous

> Могу пожертвовать Canon LBP-810, работающий через COM. Но насколько я понимаю, нужен USB принтер.

Вообще было бы очень интересно посмотреть на не-USB-принтеры. Их поддержка планируется, если, конечно, она вообще хоть кому-нибудь еще нужна. В драйвере заложена точка расширения для работы с такими железками, хотя код не написан (единственный во всем драйвере virtual, для тех, кто любит C++ критиковать :))

Yampp ()
Ответ на: комментарий от Yampp

> если, конечно, она вообще хоть кому-нибудь еще нужна

Нужна, нужна, не сумлевайтесь!

PVOzerski ★★★ ()
Ответ на: комментарий от anonymous

это как так через COM? Есть LBP-810, работающий по USB и LPT и его брат LBP-800 работающий только через LPT.

jekader ★★★★★ ()

Слава, слава! Я долго думал, зачем купил canon, а не HP! Теперь не придется выбрасывать :-)

stcondor ()
Ответ на: комментарий от Quasar

GeForce и Radeon уже поменялись местами. Нвидия сейчас вперде со своим кривым блобом, который вешает систему.

Скорость правда никакущая :(

FiXer ★★☆☆☆ ()
Ответ на: комментарий от Yampp

Попробуй версию из SVN (и ppd из SVN), там я вроде как жесткий размер строки сделал.

Попробовал, тестовая страница распечаталась отлично!

HighwayStar ★★★★★ ()
Ответ на: комментарий от HighwayStar

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

попробовал распечатать 2 страницы с текстом - распечаталось только половина первой, вторая вышла пустая

HighwayStar ★★★★★ ()
Ответ на: комментарий от Yampp

> Скажите спасибо, что я вообще ради вас что-то пишу.

Ой, хватит заливать. Ты это писал не ради кого-то (особенно, не ради sdio).

От подобных «требований» благодарности просто тошнит.

anonymous ()
Ответ на: комментарий от anonymous

> От подобных «требований» благодарности просто тошнит

как и от выпячивания своей «гордости», пишет человек - ну и пусть пишет, тебе не надо - проходи мимо, и все довольны

aho ()
Ответ на: комментарий от Yampp

То есть, можно сделать Input::Pos p = in.save_pos(); и можно сделать in.restore_pos(p); , но нельзя никаким образом узнать и/или изменить то, что в Pos хранится. Замечательная защита, и не столько от дурака, сколько от непредусмотренного кода: теперь я ТОЧНО знаю, что ЛЮБОЙ вызов restore_pos() делается СТРОГО с тем, что когда-то вернул какой-то другой вызов save_pos() и не может быть сделан с произвольным числом. Если бы я вместо Pos возвращал и принимал size_t, это дало бы возможность подставить любую лажу и потом долго искать ошибку. Сейчас круг поиска возможной лажи резко сужен - единственное, что можно спутать, это два разных Pos друг с другом. Обо всем остальном позаботится компилятор - откажется компилировать неверный код.

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

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

Теперь по поводу вашего примера. Если сущность Input у меня в программе одна (и я знаю, что она всегда будет одна) то почему бы мне вместо этой чехарды классов просто не сделать

static size_t save_pos = pos;
? У этого кода есть два недостатка. Во-первых, в нем нет ни одного класса и, если этот код будет поддерживать программист на С++, у него он вызовет разрыв шаблона. И, во-вторых, если этот код будет поддерживать обезьяна, то она может в любой момент присвоить save_pos любое значение.

Дальше. Если сущностей Input у меня может быть много, что мне мешает написать просто и элегантно:

struct input_t {
	size_t save_pos;
	size_t pos;
	/* ... */
};

static inline void save_pos(struct input_t * input) {
	input->save_pos = input->pos;
}
и вызвать:
save_pos(&input);
? Согласитесь, это более читабельно, чем
Input::Pos p = in.save_pos();
и, как минимум, не менее эффективно. Более того, можно сделать:
void work(struct input_t * input) {
	size_t save_pos = input->pos;

	/* do whatever you want with input->pos ... */

	input->pos = save_pos;
}
Вам такое в голову не приходило?

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

anonymous ()
Ответ на: комментарий от anonymous

Если сущность Input у меня в программе одна (и я знаю, что она всегда будет одна)...

Их много. Или, по крайней мере, может стать много. Статические поля использовать поэтому нельзя! Вообще, ПРИВАТНЫЕ СТАТИЧЕСКИЕ ПОЛЯ - БОЛЬШОЕ ЗЛО, потому что другой программист может быть не в курсе их существования, наивно создаст второй Input и будет ооочень долго искать ошибку. С человеком, который так делает, я в одной команде работать не хотел бы.

Когда с кодом работает несколько десятков человек, а код состоит из сотен тысяч строк, то НЕЛЬЗЯ требовать от каждого человека, чтобы он был телепатом и знал, что можно, а что нельзя делать с чужим кодом. За такими вещами ДОЛЖЕН следить компилятор.

Дальше. Если сущностей Input у меня может быть много, что мне мешает написать просто и элегантно:...

А с какого гондураса сохраненная позиция может быть только одна? А если я десять позиций хочу сохранить?

 Pos pos1 = in.save_pos(); in.read(x); Pos pos2 = in.save_pos(); in.read(y); if (! y) in.restore_pos(pos2); else if (x != y) in.restore_pos(pos1); 

... то можно работать по указателю...

Yampp ()
Ответ на: комментарий от Yampp

... то можно работать по указателю...

Вот про это я каждый раз и говорю как про падение эффективности кода. В Си выбор никуда не годный: либо открывай в полный доступ, либо обращайся по указателю. Поэтому в Си обращения к приватным полям не заинлайнятся, ведь они будут сделаны через описанную в сишке функцию, сделать их через написанный в ашке инлайн - нельзя. А в C++ инлайн может ходить в поля, недоступные другими средствами.

Yampp ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.