Поддержка C++11 в Clang

Зашел на страницу “C++11 implementation status” на сайте Clang и обрадовался. В Clang версии 3.1 появятся:

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

Сборка и тестирование проекта при помощи CMake и CTest.

Думаю что большинство C++ разработчиков так или иначе сталкивались с CMake, в то же время что такое CTest и как с его помощью можно автоматизировать модельное тестирование знают далеко не все.
Для того, что бы показать как можно использовать CMake и CTest вместе, я в качестве примера создал маленькую библиотечку, состоящую из пары строк кода, которое умеет складывать и делить Кроме приложений CMake и CTest, понадобится какая-либо UnitTest библиотека (в данном примере я взял Boost.Test, хотя, можно взять и GoogleTest). Тут стоит обратить внимание на то, что CTest не является фрэймворком для написания тестов. CTest – это приложение для запуска тестов и, если это необходимо, отправки результатов в какое-либо из поддерживаемых хранилищ. Continue reading

Классы в Rust

В Rust, зачем-то запихали классы. Видимо для того, что бы было “как у всех”. Ощущения от классов, мягко говоря, противоречивые. Хотя в рассылке и утверждают, что с тем, как классы будут выглядеть еще не определились, но ничто так не постоянно, как все временно. Итак, выглядит это дело следующим образом:

class test_class {
    priv {
        let mut val: int;
    }
    new(val: int) {
        self.val = val;
    }
    fn foo() {
        self.val += 1;
        io::println(#fmt("val+1 == %d", self.val));
    }
}

fn main() {
    let obj = test_class(10);
    obj.foo();
}

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

class.rs:14:0: 14:2 error: class with no ctor

В то же время, никаких подтверждений наличию деструкторов я не нашел. Полагаю, будут, но позже, а сейчас можно воспользоваться типом resource, пусть и будет выглядеть итоговое решение крайне криво.
Вторая не понравившаяся особенность классов, необходимость использовать self для доступа к членам из функций классов. Так же self используется для доступа к функциям класса, что выглядит как некий атавизм.
А в целом… Ну, классы, такие классы…

Перегрузка функций по параметрам. Часть 2.

В результате обсуждения в мэйл-листе Rust вопроса о перегрузке функций, родился еще один довольно занятный вариант:

enum input { int(int), str(str) }

iface to_input { fn to_input() -> input; }
impl of to_input for int { fn to_input() -> input { ret int(self); } }
impl of to_input for str { fn to_input() -> input { ret str(self); } }

fn to_input<T: to_input>(t: T) {
    alt t.to_input() {
        int(v) { io::println("int"); }
        str(v) { io::println("str"); }
    }
}

fn main() {
    to_input(5);
    to_input("hello")  
}

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

Управление памятью в Rust. Часть 2.

К сожалению, а может и к счастью, Rust не предоставляет возможности создать не управляемый объект в хипе. Так или иначе, объект будет управляться при помощи либо разделяемых, либо уникальных указателей. И в первом и во втором случаях, по входу последнего из указателей за пределы области видимости объект в памяти будет уничтожен. В довольно редких случаях, такое поведение не подходит. В качестве примера можно взять данные, передаваемые в качестве параметра для асинхронной функции в неуправляемую библиотеку.
Для решения подобной задачи остается только одно, выделить память при помощи malloc и не забыть освободить ее при помощи free. Ну и небольшой примерчик того, как облегчить себе жизнь.

unsafe fn mk_mem_obj_copy<T>(src: T) -> *T {
    let size = sys::size_of::<T>();
    let dst = libc::malloc(size);
    libc::memcpy(dst, ptr::addr_of(src) as *libc::c_void, size);
    ret dst as *T;
}

unsafe fn mk_mem_obj<T>() -> *T {
    libc::malloc(sys::size_of::<T>()) as *T
}

unsafe fn mem_obj_free<T>(obj: *T) {
    libc::free( obj as *libc::c_void );
}

Управление памятью в Rust.

Управление памятью в Rust построено довольно необычно, поэтому хотелось бы написать про него пару слов. Для начала приведу описание структуры, которая будет использоваться для наглядных экспериментов:

type test_rec = {
    fld1: uint,
    fld2: str,
    fld3: char
};

Эксперимент 1. В руководстве по Rust сказано, что компилятор самостоятельно выбирает способ передачи параметров в функцию. Данные небольшого размера, например int копируются, данные большего размера, например записи, неявно передаются по ссылке.
Continue reading

Перегрузка функций по параметрам

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

enum foo_param {
    i(int),
    s(str)
}

fn foo(param: foo_param) {
    alt param {
        i(int_val) {
            io::println(#fmt("foo was called with int == %d", int_val));
        }
        s(str_val) {
            io::println(#fmt("foo was called with str == %s", str_val));
        }
    }
}

fn main() {
    foo(i(10));
    foo(s("test"));
}

Continue reading

Когнитивный диссонанс

Вчера я был поражен на столько, что усомнился в том, во что верил уже лет 10. А именно в том, что при выравнивании 8, размер структур с полями {u64, u32, u16} и {u8, u32, u16} будет одинаков, т.е. равен 16. А ведь приложение собранное Rust утверждает что в первом случае размер 16, что и следовало ожидать, а во втором 12(!!!).

type tuple_type1 = (u64, u32, u16);
type tuple_type2 = (u8, u32, u16);

fn main() {
    io::println(#fmt("size of tuple_type1 = %u, size of tuple_type2 = %u",
        sys::size_of::<tuple_type1>(), sys::size_of::<tuple_type2>()));
    io::println(#fmt("align of tuple_type1 = %u, align of tuple_type2 = %u",
        sys::align_of::<tuple_type1>(), sys::align_of::<tuple_type2>()));
}

Содержимое консоли:

> ./main
size of tuple_type1 = 16, size of tuple_type2 = 12
align of tuple_type1 = 8, align of tuple_type2 = 8

Continue reading

Юнит-тесты и Rust

ИДея написания интегрированных юнит-тестов в Rust мне очень понравилась. Суть в том, что код тестов пишется не где-то сбоку, а в одном и том же файле что и основной рабочий код. И это еще не все! В процессе сборке, при помощи ключа компилятора –test можно запустить их на выполнение и посмотреть результаты их работы. Причем, совершенно не важно то, что у модуля нет функции main, она будет доавленна автоматически.
На практике это выглядит так:

fn foo() -> uint {
    ret 42;
}

#[test]
fn test_foo() {
    let foo_val = foo();

    assert foo_val != 0u;
}

Собираем и смотрим:

> rustc --test test.rc
> ./test
running 1 tests
test test_foo ... ok

result: ok. 1 passed; 0 failed; 0 ignored

На мой взгляд, реализация тестирования в Rust претендует на гордое звание идеальная!