8

Когда программиста отрывают от компа...



0

Клиент для Blogger своими руками-3

В предыдущем посте я начал описывать создание программки, выдающей список постов данного блога. В прошлый раз задача оказалась решенной не до конца: написанная программка распечатывала неполный список. Прежде чем приступить к внесению изменений в смысловую часть программы (то есть перед тем, как добиваться от нее того, чего я хочу), я внесу некоторые изменения в процесс тестирования. В предыдущем варианте тест класса BlogEditor (функция main(String [])) находился в самом классе. Сейчас я решил, что мне будет удобнее разместить тесты в отдельном пакете, с названием test.
Имена тестовых пакетов будут повторять имена тестируемых, за исключением того, что тестовые пакеты будут начинаться со слова test, а тестирующие классы - со слова Test. Так, для пакета blogeditutil пакет тестов будет называться test.blogeditutil, а для класса BlogEditor тестирующий класс будет называться TestBlogEditor. Класс TestBlogEditor у меня будет содержать всего один метод - main(), тело которого я просто вырежу из исходного класса (из BlogEditor). Еще добавляю всякие внешние классы через директиву import, и конечно, добавляю тестируемый класс. В результате получается следующее:

package test.blogeditutil;
import com.google.gdata.client.*;
import com.google.gdata.data.*;
import com.google.gdata.util.*;
import java.io.*;
import javax.swing.*;
import java.util.*;
import blogeditutil.*;

class TestBlogEditor
{
public static void main(String args[]) throws IOException, ServiceException
{
BlogEditor bedit = new BlogEditor();
String blogname = JOptionPane.showInputDialog(null,
"For example, if blog addresss is lotrex.blogspot.com, input \"lotrex\"",
"Input name of blog");
System.out.println("Blog posts of " + blogname + ":");
List<Entry> postlist = bedit.getPostEntryList(blogname);
for(Entry p: postlist)
{
System.out.println("Post caption: " + p.getTitle().getPlainText());
System.out.println("Date and time: " + p.getPublished());
}
}
}

Этот текст сохраняю в файле TestBlogEditor.java. Было бы неплохо запускать тесты кучкой, и при этом видеть, какой тест не прошел. Я решил использовать для этого утилиту make. Создал в корневой директории моего проекта файл makefile со следующим содержимым:
JVM = java
JFLAGS = -cp .;.\gdata-core-1.0.jar -ea -esa
test:
$(JVM) $(JFLAGS) test.blogeditutil.TestBlogEditor >res.txt

Набираю в командной строке make, жму Enter, усе работает, как и раньше :)
Теперь можно возвращаться к самой программулине :)
Получить полный список постов довольно просто, для этого в тело метода getPostEntryList() достаточно вставить одну строку. Итак, изначально реализация метода getPostEntryList() выглядела следующим образом:

public List<Entry> getPostEntryList(String blogname)
throws IOException, ServiceException
{
String blog_id = getBloggerID("http://"+blogname+BLOGGER_SERVER);
if(blog_id == null)
throw new IOException("Can't get blogID for " + blogname+BLOGGER_SERVER);
//Запрос на получение постов:
Query query = new Query(new URL (BLOGGER_FEED + blog_id + POST_LOCATION));
return bservice.getFeed(query, Feed.class).getEntries();
}

Сначала создается объект класса запроса (класса Query), которому в качестве параметра передавался URL фида. После чего мы сразу обращались к серверу за данными. Но объекту класса Query можно задать кучу параметров, один из которых - максимальное количество результатов (считай-постов) в фиде. Если мы хотим сразу скачать всю ленту целиком, но не знаем, сколько в ней постов, то просто задаем в качестве максимального значения какое-нить число побольше. Новая реализация метода getPostEntryList() немногим отличается от старой:

private final int MAX_RESULTS = 10000000;
public List<Entry> getPostEntryList(String blogname)
throws IOException, ServiceException
{
String blog_id = getBloggerID("http://"+blogname+BLOGGER_SERVER);
if(blog_id == null)
throw new IOException("Can't get blogID for "+ blogname+BLOGGER_SERVER);
//Запрос на получение постов:
Query query = new Query(new URL (BLOGGER_FEED + blog_id + POST_LOCATION));
query.setMaxResults(MAX_RESULTS);
return bservice.getFeed(query, Feed.class).getEntries();
}

Сейчас результирующий список получился больше, чем в первый раз :) Во всяком случае, первый и последний посты в результирующем списке - те же самые, что и в блоге.
Помимо заголовков постов хотелось бы еще иметь возможность прочитать их содержимое. Содержимое поста (в виде текста) можно получить из того же источника, что его заголовок и дату публикации, ведь объект Entry по сути дела и есть сам пост. Немного модифицируем наш тест (файл TestBlogEditor.java):

class QueryBlogName
{
static String getBlogName()
{
return JOptionPane.showInputDialog(null,
"For example, if blog addresss is lotrex.blogspot.com, input \"lotrex\"",
"Input name of blog");
}
}

class TestBlogEditor
{
public static void main(String args[]) throws IOException, ServiceException
{
BlogEditor bedit = new BlogEditor();
String blogname = QueryBlogName.getBlogName();
List<Entry> postlist = bedit.getPostEntryList(blogname);
System.out.println("Blog posts of " + blogname + ":");
for(Entry p: postlist)
{
System.out.println("Post caption: " + p.getTitle().getPlainText());
System.out.println("Date and time: " + p.getPublished());
}
}
}
class TestBlogEditTextContent
{
public static void main(String args[]) throws IOException, ServiceException
{
BlogEditor bedit = new BlogEditor();
String blogname = QueryBlogName.getBlogName();
List<Entry> postlist = bedit.getPostEntryList(blogname);
System.out.println("Blog posts of " + blogname + ":");
for(Entry p: postlist)
{
System.out.println("-----------------------");
System.out.println("Post caption: " + p.getTitle().getPlainText());
System.out.println("Content:");
System.out.println(p.getPlainTextContent());
}
}
}

Теперь у меня есть 2 теста класса, которые выполняют разные действия. Добавлю в конец makefile еще одну строку, что бы он тоже выполнялся. Теперь содержимое makefile выглядит так:
JVM = java
JFLAGS = -cp .;.\gdata-core-1.0.jar -ea -esa
test:
$(JVM) $(JFLAGS) test.blogeditutil.TestBlogEditor >res.txt
$(JVM) $(JFLAGS) test.blogeditutil.TestBlogEditTextContent >rescontent.txt

Тест не работает! Почему-то матершина (т.е сообщения об ошибках) исходит из того места, где вызывается метод getPlainTextContent(). Как заставить этот метод работать правильно, я так и не понял. Заглянув в документацию мы увидим, что у класса Entry есть метод getTextContent(), который возвращает объект класса TextContent, у которого есть метод getContent(), возвращающий объект класса TextConstruct, у которого есть (наконец-то!!!) метод getPlainText(), который возвращает обычную строку:

String textcontent = p.getTextContent().getContent().getPlainText();

Несколько длинновато, но зато после замены вызова getPlainTextContent() на это длинное выражение все работает. Полученный результат вряд ли можно считать читабельным, он не содержит информации ни о ссылках, ни о разметке. Было бы неплохо получить html-представление постов, дабы потом их спокойно просматривать в браузере. Как оказалось, такая возможность есть, хотя о ней прямо ничего не сказано в документации на GData API. Как оказалось (по крайней мере, для случая, когда мы получаем фид постов), объект класса TextConstruct, который мы получим при вот таком вызове:

TextConstruct textconstr = p.getTextContent().getContent();

можно спокойно приводить к типу HtmlTextConstruct, из которого уже можно получить html-представление поста. Я создал новый класс под названием TestBlogEditHtmlContent, в котором и реализовал вывод содержимого постов в html-представлении. Он отличается от предыдущего тестового класса только телом цокла for, поэтому я приведу тут лишь это различие:

for(Entry p: postlist)
{
System.out.println("<h3>Post caption:<br>" +
p.getTitle().getPlainText() + "<br>");
System.out.println("Content:<br></h3>");
HtmlTextConstruct htmlconstr = (HtmlTextConstruct)(p.getTextContent().getContent());
System.out.println(htmlconstr.getHtml());
System.out.println("<br>----------------------- end of post.<br>");
}

Все это откомпилил, и в конец файла makefile вставил еще одну строчку:

$(JVM) $(JFLAGS) test.blogeditutil.TestBlogEditHtmlContent >res.html
Теперь мона открывать файл res.html в браузере и любоваться пейзажем :)))


0

Геометрические параметры винного аромата

Сегодня после тренажерного зала, зашли в магазин купить вина. У нас начался период дегустации перед Новым годом:)
Купили JP Chenet "Medium sweet 2007" красное полусладкое. Давно собирались продегустировать что-нибудь из JP Chenet, точнее я все уговаривала мужа, поскольку те вина этого производителя, что я уже пила, оставили весьма приятно впечатление, или правильнее будет сказать послевкусие. Ну жно заметить, муж не сопротивлялся:)
Читаю этикетку: "вино крсное полуслдакое, бла-бла-бла... произведено из винограда сортов бла-бла-бла... выращеного во Франции в местечке бла-бла-бла... имеет богатый бла-бла-ба ОКРУГЛЫЙ аромат!!!
Долго по этому поводу прикалывались. Муж все пытался себе приставить геометрическую форму вкуса:)

В общем, пришли домой, открыли, налили, выпили.

И скажу я вам, аромат действительно, кроме всего прочего, ОКРУГЛЫЙ. Теперь я знаю? что это такое, но объяснить не смогу, не пытайте:)
Как говорится, лучше один раз увидеть, в смысле испробовать, чем сто раз услышать, в смысле прочитать:)

0

И снова повод есть у программистов...

Сегодня еще одна из версий дня программиста. Во всяком случае, есть повод отметить. 10-го декабря 1815-го года у поэта Джорджа Байрона и его супруги - Анны Изабеллы, которую муж за довольно сильное пристрастие к математике, ласково назвал «королева параллелограммов» появилась дочка.

Дочь сердца моего, малютка Ада!

Похожа ль ты на мать?

В последний раз, когда была мне суждена отрада,

Улыбку видеть детских синих глаз,

Я отплывал…


Байрон последний раз посмотрел на дочку, когда ей не исполнилось еще и месяца и… укатил в романтическое путешествие по Италии. «Королева параллелограммов», окончательно уставшая от мужниных причуд, сбежала к родителям и оба они представить себе не могли, что малышка Ада станет легендой кибернетики. Впрочем, мама как раз стремилась сделать все, чтобы насолить бывшему мужу, а насолить ему можно было только с математической точностью. В тринадцать лет малышка Ада уже рисовала модели летательных аппаратов, в восемнадцать встретила мужчину своей судьбы – мистера Бэббиджа. Он занимал кафедру профессора математики Кэмбриджского университета. То самое место, на котором полтора века назад работал сэр Исаак Ньютон. Вместе они придумали первый в мире компьютер и программы к нему. Деревянный «дисковод», картонные перфокарты, «процессор» на шестеренках и рычагах.

Текст самым наглым образом стащила отсюда

2

Букинистический weekend или библиотека программиста

Наконец-то мы с мужем заказали новый книжный шкаф!!! Я тут от восторга уже целую неделю прыгаю!:) и буду еще недели две прыгать, пока его не привезут:)
А пока у нас идет подготовка, которая заключется в том, чтобы разобрать старый шкаф. А для этого необходимо рассортировать книги в нем: какие-то нужно оставить дома, а какие-то можно отправить на дачу следом за шкафом:)
Нужно сказать, что старый шкаф дюже вместительный. Казалось бы не особо высокий, не глубокий - в два ряда в нем книги не встают, но вытаскивать из него тома и перевязывать их бичовкой устали жутко, заняло это у нас несколько часов. Возможно, у меня возникло такое впечатление от того, что литература все техническая и большей частью устаревшая, живой интерес во мне пробудился лишь к изданиям по фотоделу. Но химия и физика фотопечати сейчас также не актуальны, а, значит, им дорога на дачу. Буду летними вечерами, лежа в гамаке, настольгировать, вспоминая свои эксперементы в темной комнате с красной лампой при фотоувелеичителе, проявителе, закрепителе и глянцевателе:)
Но вернемся к нашим баранам (это я в накануне Курбан-байрама про них вспомнила:))А потом началось самое интересное! Разборка самого шкафа!!! В общем, когда мы отодвинули шкаф от стены, нам открылась тайна его возраста:) На его задней стенке оказался шикарнейший советский штамп, на котором среди прочих параметрв (артикул, серия и т.д.) стояла и дата изготовления данного изделия! Блин, этот монстр старше меня почти на 20 лет! Изготовлен он был аж в 1963 году!
Сделан добротно, хотя и из такого, извините, дерьма. Но вот шурупов, в отличии от прочих материалов, для него не пожалели, натыкано их было более чем достаточно(умаялись, выворачиать. Их электрический шуроповерт не брал!!!), причем до сих пор гадаем, для чего надо было заднюю фанерную стенку крепить шурапами длиной 4,5 - 5 см...
Но, слава Богу, мытарства наши закончились, сегодня и сам шкаф, и часть его содержимого перевезено на дачу!
Ждем новый! Проблем с его наполнением не возникнет. Вот и сегодня Lotrex приобрел еще одну книгу по Java "Java без сбоев" Стивена Стелтинга.

2

Клиент для Blogger – своими руками-2

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

После прочтения доки на GData API выяснилось, что список постов будет иметь тип List<Entry>, где Entry - специальный класс, описывающий 1 элемент фида.
Сказано-сделано, скоро был готов примерно такой код:

package blogeditutil;

import com.google.gdata.client.*;
import com.google.gdata.data.*;
import com.google.gdata.util.*;

import java.io.*;
import java.net.*;
import javax.swing.*;
import java.util.*;

public class BlogEditor
{
public static void main(String args[]) throws IOException, ServiceException
{
BlogEditor bedit = new BlogEditor();
String blogname = JOptionPane.showInputDialog(null,
"For example, if blog addresss is lotrex.blogspot.com, input \"lotrex\"",
"Input name of blog");
System.out.print("Blog posts of " + blogname + ":\n");
List<Entry> postlist = bedit.getPostEntryList(blogname);
for(Entry p: postlist)
{
System.out.println("Post caption: " + p.getTitle().getPlainText());
System.out.println("Date and time: " + p.getPublished());
}
}
}

Однако, он (конечно) не компилируется, поскольку метод getPostEntryList(String) в классе BlogEditor не реализован. Я написал код, использующий метод getPostEntryList раньше, чем этот метод был реализован. Немного странно, что я использую интерфейс класса (т.е. его методы) раньше, чем я их реализовал или даже объявил. Но как иначе я могу определить, какой интерфейс будет наиболее удобным? Имхо, самый простой способ придумать интерфейс для класса - написать программку, в которой вы заставляете класс делать то, чего хотите от него добиться, с помощью интерфейса, который (пока) не реализован. При этом я придумываю методы, думаю о том, какие параметры эти методы должны принимать и какие действия они должны выполнять. Так, постепенно у меня формируется представление о том, каким должен быть интерфейс класса (конец лирического отступления, прочитанного менторским тоном:)))

Изучая доку на GData API, я пришел к выводу, что реализовать метод getPostEntryList(String blogname), где параметр blogname - имя блога (в моем блоге это будет строка "lotrex"), используя только интерфейс GData API, и без авторизации, невозможно. Не знаю, в чем тут причина - то ли я чего недосмотрел, то ли разработчики GData API, то ли это политика у Google такая... Но хочется все же иметь возможность читать посты блогов, которые вам интересны.

Для того, чтобы получить фид постов, необходимо знать blogID - строку из нескольких десятичных цифр (чем моложе блог, тем она длинее). Ну что ж, предположим что у нас есть метод getBloggerID(String blogaddr), возвращающий blogID, и попробуем этот blogID распечатать, а использование getPostEntryList() пока уберем:

public static void main(String args[]) throws IOException, ServiceException
{
BlogEditor bedit = new BlogEditor();
String blogname = JOptionPane.showInputDialog(null,
"For example, if blog addresss is lotrex.blogspot.com, input \"lotrex\"",
"Input name of blog");
System.out.println("Blog ID of " + blogname + ":");
System.out.println(bedit.getBloggerID("http://" + blogname +
".blogspot.com"));
}

Пока это все не компилируется - нужно реализовать метод getBloggerID(). В GData API получение blogID без авторизации не предусмотрено, увы :( Строка blogID имеется в коде главной странице блога (пока что), ее легко обнаружить, поискав выражение вида "blogID=XXXXXXXXX". На мой взгляд, этот способ получения blogID вряд ли можно назвать хорошим из-за того, что он в любой момент может перестать работать. Но иного пути, похоже, нет :(((
Поэтому я с (тяжелым сердцем :))) написал довольно простой метод(а точнее - 2 метода), в котором применил именно этот способ:

private String getBloggerID(String blogaddr) throws IOException,
SocketTimeoutException
{
URL blogurl = new URL(blogaddr);
URLConnection connection = blogurl.openConnection();
boolean isconnect = false;
for(int i=0; i < 10; i++)//10 попыток соединения
{
try{
connection.connect();
}catch(SocketTimeoutException ex){
continue;
}
break;
}
connection.connect();
//Создаем класс BufferedReader для чтения по строкам:
BufferedReader bread = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String str="";
//Читаем ответ сервера по строкам и производим разбор:
//Искать строку, содержащую blogID=xxxxxxx, где xxx - любая цифра
Pattern pat = Pattern.compile("blogID=([\\d]+)");
while( (str = bread.readLine()) != null)
if( (str = getMatch(pat, str)) != null)
break;
bread.close();
//Искать строку, содержащую только цифры:
pat = Pattern.compile("[\\d]+");
return getMatch(pat, str);
}
private String getMatch(Pattern pat, String str)
{
Matcher match = pat.matcher(str);
if(match.find())
return match.group();
return null;
}

Метод длинноват, требует укорачивания, но пока оставим его таким. Чтобы он работал, нужно подключить пакет поддержки регулярных выражений:
import java.util.regex.*;

Компилируем (файл gdata-core-1.0.jar должен находится в текущей директории):
javac -cp .;.\gdata-core-1.0.jar -encoding Cp1251 .\blogeditutil\BlogEditor.java

Если все ОК, то можно запускать:
java -cp .;.\gdata-core-1.0.jarblogeditutil.BlogEditor

Ура, первый шаг сделан - что-то работает, и даже выдает какие-то цифирки, возможно даже, что эти цифирки - тот самый blogID, ради получения которого было написано так много букфф :))) Что ж, теперь у меня получится написать метод, выдающий список блогов:

private final String BLOGGER_FEED = "http://www.blogger.com/feeds/",
POST_LOCATION = "/posts/default",
BLOGGER_SERVER = ".blogspot.com";
private GoogleService bservice;
public BlogEditor()
{
bservice = new GoogleService("blogger", "lotrex-BlogEditor-0.0");
}
public List<Entry> getPostEntryList(String blogname)
throws IOException, ServiceException
{
String blog_id = getBloggerID("http://"+blogname+BLOGGER_SERVER);
if(blog_id == null)
throw new IOException("Can't get blogID for " + blogname+BLOGGER_SERVER);
//Запрос на получение постов:
Query query = new Query(new URL (BLOGGER_FEED + blog_id + POST_LOCATION));
return bservice.getFeed(query, Feed.class).getEntries();
}

Теперь можно заставить работать мой первый вариант метода main(), распечатывающий этот список (тот main(), что в начале статьи). Вставляю его целиком, а предыдущий метод main() закрываю комментами. Компиляция проходит нормально. Запускаю (вывод программки перенаправляю в файл, поскольку на консоль выводятся нечитабельные краказяблы):
java -cp .;.\gdata-core-1.0.jarblogeditutil.BlogEditor >res.txt

Ура, список постов получили!!! Поэкспериментировав с этой программкой, можно заметить, что список выводится не полностью. Когда я опробовал эту прогу впервые, для всех блогов выводилась информация о 15 последних постах (или меньше), счас - о 25(или меньше). Интересно, что теперь, когда я запускаю прогу с моего компа, это число совпадает с числом постов в моем блоге, а раньше это было не так. Возможно, тут дело в настройках блога, а может, и нет :) Думаю, выяснить причину будет несложно - исходные коды GData API открыты. Имхо, это не столь уж важно. Важнее то, что в документации не сказано, как узнать общее количество постов в блоге. Но об этом напишу в следующий раз - эта статья и так получилась слишком длинной.

8

Водопады долины реки Аше







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

Картинки клмкабельны

6

Об использовании словоформ

Недавно, (на прошлой неделе в пятницу) выходя с работы, я обнаружил на доске объявлений приказ интересного содержания. Выкладываю его здесь :)

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

Клиент для Blogger-своими руками

Знаю, знаю... Мне счас скажут: "на хрен нужно, скачай тут или там-то и пользуйся".
Первый коммент имхо, должен быть об этом :) Сразу хочу ответить, что никого не заставляю
писать такой клиент своими руками, особенно кому это не интересно.
Я хочу его написать, потому что:
1) я люблю писать программы.
2) мне писать программы интересно.
3) см. пункт первый.
Начать нужно пожалуй с того, почему я на это решился. При хождении по справке Блоггера я натыкался на ссылки "для разработчиков" (счас уж точно не помню). Да и сам предполагал, что какая-то поддержка подобного рода должна быть. После 2-х минутных поисков попал на страничку
Client Libraries and Sample Code, где предлагалась поддержка для различных языков.

К моему сожалению, либы для C++ не оказалось (видимо, для ее создания необходимо слишком много работы "ручками"). Выбрал Java (пока что в упор не хочу замечать .NET - слишком тяжелый у этого farmework-а дистрибутив, а так же из религиозных соображений :))) )
Фразочка на этой страничке: "For each language, the client library provides tools
and an abstraction layer, letting you construct queries and use response data
without having to create HTTP requests or process HTTP responses by hand." -
ввергла меня в состояние легкой эйфории - супер, если это правда, то решить
поставленную задачу будет довольно легко.
Итак, идем на Getting Started Guide. и смотрим, чего нам нужно качать. Насколько я понял, далеко не все из этого будет использоваться для blogger-клиента. Похоже, что пакет, который Google предлагает разработчикам (Google data API), обладает гораздо большими возможностями, и кроме blogger-клиента можно много всего понаделать, не мешал бы только геморрой и вредные привычки :)))
Сначала там предлагают качнуть JDK, но если мы пишем на Java, то оно ужо должно у нас быть (на всяк случай - посмотрите, добавлен ли у вас в Path путь к java-компилятору. Если нет - обязательно добавьте). Потом треба качнуть Apache Ant версии не ниже 1.7.
Если этот зверь уже есть- энто хорошо, а то весит он аж 11 МБ. Странный зверь,
в данном случае он нужен только для сборки библиотеки - то есть по сути, используется
как make. Люди добрые, расскажите мне плиз, чего он еще хорошего делает
(просто хочется знать, неужто я скачал всего лишь аналог make весом в 11 МБ?)...
Я просто скачал архив, распаковал его в папку D:\Ant (можно в любую другую, но
_весьма желательно_, чтобы имена всех папок в пути соответствовали формату 8.3).
Чтобы зверь Ant заработал и не гавкал, надо установить переменные окружения ANT_HOME
(в моем случае она будет иметь значение D:\Ant) и JAVA_HOME (в моем случае D:\jdk5).
И в этих переменных также нежелательны длинные имена папок, а уж тем более всякие
двойные кавычки. Потом качаем JavaMail API и JavaBeans Activation Framework.
Кроме этого, будет нужна библиотека servlet-api.jar. Она входит в состав сервлет контейнеров J2EE, Tomcat, WebLogic и т.п. Я скачал отсюда zip-архив Tomcat и выдернул оттуда соответствующий файл.
Теперь можно приступать к установке самого Google Data API. Заходим сюда и скачиваем последнюю версию библиотеки в исходниках - файлы gdata-src.java-1.x.x.java.zip и gdata-samples.java-1.x.x.java.zip. Распаковываем все это в одну папочку, после чего ищем файл gdata/java/build-src/build.properties . Изначально выглядит он следующим образом:

# Points to a external library dependancies
# EDIT-THIS: If rebuilding the authsub or gbase/recipe sample point to,
# servlet jar in Sun's Servlet API library.
servlet.jar=/tmp/gdata_dep/servlet-api-2.4.jar
# EDIT-THIS: Point to mail.jar lib in Sun's Java Mail API.
mail.jar=/tmp/gdata_dep/mail.jar
# EDIT-THIS: If using version older than JDK 1.6,
# Point to activation.jar in Sun's activation framework library.
activation.jar=/tmp/gdata_dep/activation.jar

# Include debugging information in built library files. Possible values "on" or "off"
javac.debug=true
javac.debuglevel=lines,vars,source

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

# Points to a external library dependancies
# EDIT-THIS: If rebuilding the authsub or gbase/recipe sample point to,
# servlet jar in Sun's Servlet API library.
servlet.jar=c:\\Java\\libs\\servlet\\servlet-api.jar
# EDIT-THIS: Point to mail.jar lib in Sun's Java Mail API.
mail.jar=c:\\Java\\libs\\javamail-1.4.1\\mail.jar
# EDIT-THIS: If using version older than JDK 1.6,
# Point to activation.jar in Sun's activation framework library.
activation.jar=c:\\Java\\libs\\jaf-1.1.1\\activation.jar

# Include debugging information in built library files. Possible values "on" or "off"
javac.debug=true
javac.debuglevel=lines,vars,source

Почти все, остался последний штрих - для того, чтобы примеры работали, нужно отредактировать
файл gdata/java/build-samples/build.properties: sample.credentials.username и sample.credentials.password нужно присвоить правильные значения вместо тех, что стоят по умолчанию. Теперь можно запускать компиляцию примеров.
Как и сказано в руководстве по инсталляции, я зашел в директорию gdata\java и запустил сначала компиляцию примера для календаря:

ant -f build-samples.xml sample.calendar.run

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

ant -f build-samples.xml sample.blogger.run

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

2

Немного о провинциальных изготовителях печатных плат


Давно хотел написать эту заметку, потому что наболело. Начну с того, что работа моя связана не только с программами, но и с железом, поскольку я и еще несколько человек в моем отделе занимаемся разработкой приборов. А так как народу мало, частенько приходится брать на себя функции снабженца, искать что-где-почем продается, где те или иные детали можно изготовить. К сожалению, за последние 3-5 лет "стабильность" в нашей стране привела к тому, что в моем городе (Казань) печатные платы уже никто не делает. Поэтому, если необходимо изготовить более-менее сложный прибор, для которого печатные платы "на коленках" сделать нереально, то приходится обращаться в другие города, где есть соответствующее производство.

Сразу скажу, что "быстро" хорошие платы не делаются, и всякое там "экспресс-изготовление за 3-7 дней" является в большинстве случаев либо экспресс-изготовлением брака, либо уже "не-экспресс". Поэтому в такие фирмы мы даже и не обращались (в прошлом был печальный опыт). Неплохие платы получались почти у всех фирм, с кем мы работали, при сроке изоготовления 3-4 недели. Еще один немаловажный параметр - цена. Сей параметр сильно зависит от города, где находится производство. Цены на платы в Москве, Питере и Новосибирске оказались практически одинаковыми (разница 1-2%) и очень высокими. Гораздо ниже цены оказались в провинциальных городах. Так, цена одного и того же комплекта плат в Ижевске оказалась ниже чем Москве примерно в 2.5 раза! Не скажу, что заказывать платы в Ижевске (см. их сайт и контактная информация) лучше, чем где бы то ни было. Порой достучаться по мылу и дозвониться до них бывает тяжеловато. Но если вы до них достучались и они приняли ваши платы в производство - можете быть уверены, что:
1) эти платы будут сделаны раньше, чем вы заплатите за них деньги.
2) платы к вам приедут сами, если вы не забудете попросить, чтобы в счет включили стоимость доставки
3) платы будут отличного качества.
В начале сентября я как раз закончил разводить платы для очередного опытного образца прибора. Я конечно, стал стучаться в Ижевск, но они сказали что завалены гос. заказами и приступят к нашему заказу не раньше, чем через месяц. Переплачивать столичным "печатникам" мне не хотелось, и я стал искать других "провинциалов", готовых взяться за мою плату. Вскоре таковые нашлись в Саратове, - завод агрегатного машиностроения СЭПО-ЗЭМ. Заказ у меня быстро приняли, и 11 сентября прислали счет на вполне приемлемую сумму. Все бы хорошо, но люди, с которыми я разговаривал, отказались включить в счет доставку. Я (вот дурак!!!) согласился на их условия - доставить платы до Казани через транспортную компанию.
Примерно через 3 недели платы были готовы. Теперь нужно было их доставить в Казань.
Вот тут началось самое интересное. Оказалось, что организовать доставку через транспортную компанию довольно сложно - для этого надо оформить и подписать кучу документов. Поэтому я сначала решил организовать доставку через моих родственников, живущих в Саратове. Увы, эта попытка провалилась, и пришлось идти честным путем - оформлять договор с транспортной компанией.
Офомление договора с "Грузовозофф" и заявки на доставку груза(млин, а весь груз - 9 печатных плат общим весом около пол-кило) заняло у меня 4 дня - почти полная рабочая неделя. Кроме этого, мне необходимо было отправить на завод СЭПО-ЗЭМ доверительное письмо, в которым я подтверждал, что доверяю компании "Грузовозофф" получение моих грузов у завода "СЭПО-ЗЭМ". С представителями завода была достигнута договоренность о том, что платы они отдадут по получению от меня факсимильной копии такого письма, а оригинал я пошлю почтой. Все условия я выполнил, письмо факсом отправил, и получил подтверждение от них том, что факс дошел. Вроде все ОК, остается только ждать. Прошло более недели, и я решил узнать, как обстоят дела. После звонка саратовским "печатникам" выяснилось, что экспедитор из "Грузовозофф" к ним за платами заезжал, но платы ему не дали, потому что якобы доверительного письма от меня они не получали и "никто это письмо в глаза не видел". Это было уже слишком! Я с трудом удержался от непарламентских выражений и сказал, что у нас факт посылки такого письма факсом зарегестрирован. После чего письмо сразу нашлось. Я не знаю, какое слово лучше характеризует такую ситуацию на этом заводе, но имхо, наиболее подходящее - "бардак".
Сегодня платы наконец отгрузили экспедитору "Грузовозофф". Я думаю, они доедут до нас через неделю. Со дня оплаты счета на печатные платы прошло 2 месяца с лишком. Ради чего я все это писал? Просто хочется предупредить: люди добрые, если вам дороги нервы и время - не связывайтесь с саратовским заводом СЭПО-ЗЭМ!
4

Выходной программиста





1

Поиграем!

Теперь мой ход! Передо мной ходила BeBeKK@
:) Правила игры следующие:
получаете букавку от предыдущего участника, вспоминаете литературных персонажей имена, прозвища которых начинаются именно на эту буковку, выбираете пять наиболее близких Вам по духу или самых ярких, даете краткое описание и иллюстрацию.

Мне досталась буква "С".
Итак:



1. Сантьяго - главный герой повести Эрнеста Хемингуэя "Старик и море", вышедшей в 1952 году.
Замысел этого произведения созревал у Хемингуэя в течение многих лет. Еще в 1936 году в очерке «На голубой воде» для журнала «Эсквайр» он описал подобный эпизод, случившийся с кубинским рыбаком. Сама же повесть была опубликована в сентябре 1952 года в журнале «Лайф».



2. Санчо Панса (исп. Sancho Panza) — персонаж романа Мигеля Сервантеса «Хитроумный идальго Дон Кихот Ламанчский», оруженосец Дон Кихота. На протяжении романа активно употребляет в речи пословицы, являющиеся составной частью так называемых санчизмов — монологов, произносимых Санчо. Фамилия Панса, по-испански записываемая также Pança, означает «брюхо». В испанском литературоведении рассматривается как олицетворение испанского народа.


3. Джон Сильвер (англ. John Silver) — вымышленный пират XVIII века, персонаж романа Роберта Льюиса Стивенсона «Остров сокровищ».
Сильвер имел прозвища: «Одноногий», «О'корок», «Долговязый Джон». У него нет одной ноги. На его плече большую часть времени сидит попугай по кличке «Капитан Флинт», который время от времени выкрикивает: «Пиастры, пиастры, пиастры!»
В известных экранизациях Джона Сильвера играли Осип Абдулов (фильм 1937 года), Борис Андреев (фильм 1971 года), Олег Борисов (фильм 1982 года), Армен Джигарханян (мультфильм 1988 года), Чарлтон Хестон (фильм 1990 года).



4. Снегурочка - героиня пьесы А.Н. Островского. В ней Снегурочка предстаёт как дочь Деда-Мороза и Весны-Красны, которая погибает во время летнего ритуала почитания бога Солнца Ярилы. Имеет вид прекрасной бледной светловолосой девушки. Одета в бело-голубую одежду с меховой опушкой (шубка, меховая шапка, рукавички). Первоначально пьеса не имела успеха у публики.
В 1882 году Н. А. Римский-Корсаков поставил по пьесе одноимённую оперу, которая имела громадный успех.



5.Салтан - главный герой пушкинской "СКАЗКА О ЦАРЕ САЛТАНЕ, О СЫНЕ ЕГО СЛАВНОМ И МОГУЧЕМ БОГАТЫРЕ КНЯЗЕ ГВИДОНЕ САЛТАНОВИЧЕ И О ПРЕКРАСНОЙ ЦАРЕВНЕ ЛЕБЕДИ"

Эти строчки все мы помним с детсва:

Три девицы под окном
Пряли поздно вечерком.
«Кабы я была царица, —
Говорит одна девица, —
То на весь крещеный мир
Приготовила б я пир».
«Кабы я была царица, —
Говорит ее сестрица, —
То на весь бы мир одна
Наткала я полотна».
«Кабы я была царица, —
Третья молвила сестрица, —
Я б для батюшки-царя
Родила богатыря».

Только вымолвить успела,
Дверь тихонько заскрыпела,
И в светлицу входит царь,
Стороны той государь.
Во всё время разговора
Он стоял позадь забора;
Речь последней по всему
Полюбилася ему.
«Здравствуй, красная девица, —
Говорит он, — будь царица
И роди богатыря
Мне к исходу сентября.


Вот:)

А еще хотелось написать. да правила не позволяют, про Садко, Самгина, который Клим, Сюзанну из "Женитьбы Фигаро", про стойкого оловянного солдатика, Стародума из "Недоросля" Фонвизина, Софью из грибоедовского "Горе от ума", про Сонечку Мармеладову...

З.Ы. Беззастенчиво пользовалась материалами с Википедии
2

Книжечки для жены программиста


Купила я сегодня себе книжечку, да не простую, а как всегда для работы. Точнее для того чтобы повышать свою квалификацию верстальщика:) А книжечка называется "Скрипты в InDesign. Руководство для умных дизайнеров и ленивых верстальщиков", автор - Михаил Борисов. Не буду льстить себе и называть себя умным дизайнером,
а вот в том, что я ленивый верстальщик, признаюсь, положа руку на сердце:) Но самое интересное в том, что книжечка хоть и про InDesign, но большей частью про программирование, про написание скриптов для Indesign.
Много лет работала в этой программе и не знала к своему стыду ни про какие скрипты, все делала ручками. А вот угораздило меня выйти замуж за программиста и
книги мне теперь все странные какие-то попадаются: по моей тематике, но с уклоном в программирование. Может, программирование - это заразно?
Просто когда я изучала HTML и CSS, я об этом не задумывалась. Но теперь когда выбираю язык для изучения(выбираю между JavaScript и Visual Basic), на котором планирую ПИСАТЬ (страшно представить)эти
самые скрипты, прихожу к выводу, что программирование - это не сумасшествие, т.к. с ума сходят поодиночке, программирование - это какое-то эпидемиологическое заболевание, вакцины от которого нет, а иммунитет на него выработался далеко не у всех...
0

IBM developer Works

Недавно (в пятницу, 7 ноября) я искал по дневникам единомышленников (то бишь народ, пишуший на Java и про Java). Наткнулся на дневник Xatkaru. Последний ее пост (Утечки памяти в Java) меня даже несколько напряг одним своим названием (как, в Java бывают утечки памяти???). Через ссылку в этом ее посте я попал на IBM developer Works, чему очень рад: я давно не встречал столько статей по интересующей меня тематике, сколько нашел там. К сожалению, оставить коммент у нее в блоге не получилось: почему-то комментить там можно только пользователям ЖЖ, а мне даже через OpenID оставить коммент не удалось. И поэтому пишу только тут: спасибо вам, Xatkaru, за ваш пост и ссылочку на интересный ресурс.
6

Компилятор с Java в “родной” код - “на коленках”?

Я на работе почти доделал свой долбаный отчет, и решил устроить себе маленький отдых. В блог долго ничего не писал, поскольку хотелось выдать чо-то такое СУРЬЕЗНОЕ и ИНТЕРЕСНОЕ, в результате ни того, ни другого родить не удалось.

Как обещал в предыдущем посте, объясню, для чего могут понадобиться вещи, описанные мной в постах о SmartPointers и исключениях. Дело в том, что мне нравится Java, пожалуй, даже слишком. Не буду заниматься тут ее рекламой, поскольку сравнение языков - дело неблагодарное. Но Java-программа не может выполнятся без окружения(JRE), а дистрибутив JRE весит 17 Мб. Не хухры-мухры, даже если вы не на диалапе сидите. Моя голубая мечта - компилятор Java-программ в "родной" исполняемый код той платформы, которую я использую. Увы, GNU Java слишком урезана, что бы ее можно было использовать, да и одна важная вещь - шаблоны там пока не поддерживаются. Разжиться такой штукой, как Excelsior Jet мне совсем не по карману.
Посему стали посещать такие мысли - а может, просто преобразовать программу, написанную на Java в прогу на C++? Что в C++ такого, чего нет в Java? На первый взгляд мне показалось, что все очень просто, ведь C++ - охрененно мощный язык. Там есть.... куча всего нужного. И не очень.
Короче, разворачивание стека вызовов, как в Java, на C++ эмулировать просто. Простейший сборщик мусора "для бедных" - тоже. Как мне кажется, можно даже сравнительно просто сделать дефрагментацию кучи. Если кому интересно, каким образом - постараюсь изобразить реализацию. В тупик меня поставил Reflection API. Я всегда пытаюсь избегать категоричных утверждений, но похоже, что Reflection API нельзя эмулировать с помощью той RTTI, которая есть в C++ на сегодняшний день. В черновике стандарта C++ от 19.10.2005 класс type_info описан так:

namespace std{
class type_info {
public:
virtual ~type_info ();
bool operator ==( const type_info & rhs ) const ;
bool operator !=( const type_info & rhs ) const ;
bool before ( const type_info & rhs ) const ;
const char * name () const ;
private :
type_info ( const type_info & rhs );
type_info & operator =( const type_info & rhs );
};
}

Видно, что за просто так вы вряд ли что-то получите, окромя названия типа в виде текстовой строки. Не говоря уже о таких вещах, как получение различных конструкторов типа и их использования для создания экземпляров или получения списка доступных полей и методов. Короче, нужно что-то такое свое ваять, дабы получить требуемый эффект. Причем этот стандарт не требует, что бы функция const char* name() возвращала уникальную для каждого типа строку.
В последнем стандарте (черновик C+0X от 4.10.2008) ситуация стала получше. Там класс type_info описан следующим образом:

namespace std {
class type_info {
public:
virtual ~type_info();
bool operator==(const type_info& rhs) const;
bool operator!=(const type_info& rhs) const;
bool before(const type_info& rhs) const;
size_t hash_code() const throw();
const char* name() const;
type_info(const type_info& rhs) = delete; // cannot be copied
type_info& operator=(const type_info& rhs) = delete; // cannot be copied
};
}

Появилась, к счастью, функция size_t hash_code(), про которую английским по белому накорябано:
"Returns: an unspecified value, except that within a single execution of the program, it shall return the same value for any two type_info objects which compare equal.
Remark: an implementation should return different values for two type_info objects which do not compare equal".
То бишь, если два объекта класса type_info по результатам сравнения оказались не равны, эта функция для разных объектов должна возвращать разные значения, а если равны - то одинаковые.
Сначала хотел закончить этот пост пессиместическим резюме о том, что мои надежды рухнули, эмулировать Reflection API на C++ не получится (помидоры завяли, солнце не светит, и т.п). Но некоторые весчи меня обнадеживают. Так, класс type_info поддерживается библиотекой, и возможно, я смогу его переопределить и добавить в него функцию hash_code. Кроме этого, я заглянул в исходники самого Java Reflection API. Похоже, реализация его сделана в основном на Java, а не зашита полностью в виртуальную машину. Так что есть что почитать, о чем подумать, авось какие мысли появятся.
8

Вот что бывает...

когда моим фотоаппаратом снимаю не я:











2

О пользе блогов для блогописателей и о будущем

На прошлой неделе выяснилось, что срочно нужна копия отчета, который я даже не начинал писать. Я никогда не считал себя графоманом, поскольку перспектива излияния объемного текста за заданное время приводит меня в ужас. Вчера начал потихоньку что-то кропать ручкой на листочках :)))) Удивительное дело - до того как я начал вести блог, я из себя выдавливал текст по капле (ну, практически, по букве). Счас стало гораздо легче - похоже, ведение блога стимулирует в человеке какие-то литературные навыки (если можно так выразиться :))) ) И посему, я надеюсь, что напишу-таки этот долбаный отчет к концу недели.
Теперь немного о будущем. Очень надеюсь, что наконец-то напишу о моих мучениях с Java Reflection API, когда я пытался проанализировать внешний по отношению к моей программе класс(т.е. этот класс не являлся частью моей программы). Как оказалось, это непросто, но вполне решаемо. Причем решил я эту проблему очень "криво", совсем не так, как это нужно было делать (как было нужно, я выяснил совершенно "нечаянно" и гораздо позже).
Наверняка те, кто иногда ко мне заходит, заметили что я начал выдавать странные длинные посты, вызывающие законный вопрос - "а на хрена это надо??" (я имею в виду мои последние творения о SmartPointers и исключениях). Вот хочу еще написать и о том, зачем все это мне надо. Глядишь, и еще кто-нибудь заинтересуется.
6

Финансовый аспект фотопечати в домашних условиях (Личный опыт)

Недели на две, а то и на месяц меня лишили фотоаппарат, а это значит, что появилось время обработать и напечатать отснятый материал.
Если с обработкой все ясно, нужно наличие компьютера, графического редактора и желание заниматься обработкой фото, то с печатью дело обстоит иначе. Конечно, есть огромное число фотосалонов, где быстро и относительно недорого напечатают любое количество снимков. Но это только на первый, неискушенный взгляд.
У меня лежит компашка с фотоматериалом с нашей свадьбы, на ней записано порядка 900 кадров, около 700 из них хочу отпечатать. В среднем цена по Казани - 5 рублей за один отпечаток 10х15. Значит, я отдам около 3500 рублей. Пусть даже с 10% скидкой это составит 3150 рублей. отдать такую сумму в раз, тем паче не зная, что я получу на выходе, мне жалко. Сколько бы не уверял меня сотрудник салона, что будет применена ручная коррекция к каждому кадру, не поверю: за час-два 700 кадров ручками не обработаешь КАЧЕСТВЕННО, а именно за это время обещали выполнить мой заказ. Вспоминается по такому поводу анекдот о том, как в Китае снимают цветное кино.
Думаю, каждый, кто периодически отдает в печать цифровые фото, не раз, получив отпечатки, оказывался разочарован в результате.
Вот и я, несколько раз попав в подобную ситуацию, пришла к выводу, что фотопроцесс нужно контролировать самой от начала и до конца. И пару лет назад приобрела бюджетный струйный фотопринтер HP DeskJet 4163.
Вероятно мне возразят, мол, печатать дома на своем принтере дороже, на одних картриджах разоришься. Действительно набор картриджей для фотопечати в моем случае потянет на 2000 рублей, это около 100 отпечатков 10х15 в лучшем случае. Да еще за фото бумагу HP глянцевую 240 г/м 100 карточек 10х15 придется выложить около 800 деревянных. Суммируем расходы и делим их на количество отпечатков на выходе (в идеале 100), получаем: 2000/100+800/100 = 20 рублей (стоимость чернил) + 8 рублей (стоимость бумаги) = 28 рублей за фото. Конечно, это ОЧЕНЬ дорого!!!
Что ж будем думать дальше...

По весне приобретала я в одном фотомагазине нейтральный фотофильтр, разговорилась с продавцом и он, поинтересовавшись моим принтером, предложил мне чернила для заправки картриджей. Тогда мне было, прямо скажу, не до этого и отметив для себя, где и что можно купить, довольная покупкой покинула сие заведение. Но по мере накопления материала и осознания того, что есть кадры, которые достойны вывода на печать, мысли о том, как сделать струйную печать в домашних условиях дешевле, вернулись.
Не мудрствуя лукаво, купила предложенные мне ранее чернила фирмы Skyhorse, получила краткие инструкции по заправке и отправилась на работу ожидать окончания рабочего дня, дабы провести хирургическое вмешательство в самое сердце моего принтера. Вопросы сохранности работоспособности принтера меня не заботитили, поскольку очевидным плюсом HP является сменный картридж, а не только чернильница (убью картридж - выкину, куплю новый), вот такое вот сменное сердце. К тому же срок гарантийного обслуживания принтухи вышел.
Итак, я купила 6 100-миллилитровых пузырьков с чернилами для трех цветного картриджа, фотокартриджа и черного картриджа. Отдала за этот набор около 900 рублей. Прикинем. На одну заправку картриджа № 134 чернил каждого цвета вливаем не более 7 миллилитров, на черный картридж № 129 потратим около 10 миллилитров чернил, тоже с фотокартриджем, проведя несложные арифметические расчеты, получим 9-10 заправок. Соответственно стоимость одной заправки 90-100 рублей. Теперь стоимость заправки поделим на количество отпечатков (100 штук 10х15 с одного набора заправленных картриджей), получаем стоимость чернил необходимых для печати фотографии размера 10х15 равной примерно 1 рубль. По разным данным картридж способен пережить от 5 до нескольких десятков заправок.
Таким образом, по чернилам экономия у меня составила 20 рублей при оригинальном картридже к 1 рублю при заправленном, то есть 95%.
С бумагой стороннеого производителя экономия тоже может получиться приличной, но поскольку мне пока не до экспериментов, что-либо рассказать о стоимости и качестве бумаги не HP не могу. К тому же эксперименты тоже не бывают дешевы.
Но так как отдавать 8 рублей за лист 10х15 меня жаба душит, я попыталась сэкономить, не отказываясь от фирменной фотобумаги. Поскольку передо мной стоит задача распечатать свадебные фото, то выбор мой пал на глянцевую бумагу 240-250 г/м2. В ближайшем магазине "Эльдорадо" я купила пачку глянцевой бумаги НР (А4, 50 листов, 250г/м2) за 779 рублей. С листа формата А4 (297х210 мм, а реально 296х209мм) получаем 4 карточки 148х105мм, это практически 10х15 см, значит, с 50 листов А4 получим 200 листов 10х15. А это в свою очередь получается меньше 4 рублей за 1 фото.
По бумаге экономия у меня составила около 50%.
В итоге отпечаток 10х15 в домашних условиях у меня вышел стоимостью: 1 рубль (цена необходимых чернил) + 3 рубля 90 копеек (цена 1 листа глянцевой фотобумаги НР 250 г/м2) = 4 рубля 90 копеек.
Что ж меня этот вариант вполне устраивает. Это примерно на 80% дешевле, чем при печати с оригинальными картриджами, и это не дороже, чем в салоне (в итоге 3450 рублей), зато ручная коррекция каждого снимка, полный контроль процесса печати и уверенность в результате. Ну, не без риска, конечно,но, по мне, так игра того стоит. К тому же остается множество возможностей еще больше удешевить печать фото в домашних условиях: это, во-первых, эксперименты с бумагой, а во-вторых, у меня появились мысли в дальнейшем попробвать СНПЧ, вот только жаль для НР их почти не бывает.
2

Метки для координатных осей

При рисовании графиков нередко встает такая задача: мы хотим, что бы на координатных осях графика у нас в нужном месте отображались метки, причем метки должны иметь "круглые" значения. Например, по оси X у нас минимальное значение 0.2259, а максимальное 0.2372, тогда метки должны стоять в точках 0.2260, 0.2280, 0.2300, 0.2320, 0.2340 и 0.2360 (шаг 0.002). Я уже давно использую одну функцию, решающую эту задачу. На вход она получает интервал (x1, x2) и приблизительное число меток, а возвращает массив, содержащий значения меток. Реализация следующая:

#include <stdio.h>
#include <vector>
using namespace std;

typedef vector<double> TDoubleArray;
//Вычисление значений, на которых должны проставляться метки на координатных осях.
static TDoubleArray & CalcGridLabels(double xmin, //минимальное значение.
double xmax, //максимальное значение.
int N) //количество меток в указанном интервале +/-2
{
#define MANT_NUM 5
double mant[MANT_NUM] = {0.1, 0.2, 0.25, 0.5, 1.0},
dx = xmax-xmin;
int i;
//Подбираем значение мантиссы, близким к величине dx:
while(dx < mant[0])
for(i=0; i < MANT_NUM; i++)
mant[i] /= 10.0;
while(dx > mant[MANT_NUM-1])
for(i=0; i < MANT_NUM; i++)
mant[i] *= 10.0;
//Если количество меток > 10:
while(1)
{//Первый проход:
for(i=MANT_NUM-1; i >=0 ; i--)
if(dx/mant[i] >= N )
break;
if(i >= 0)
break;//все хорошо, N меток будет.
//Умножаем метки на 2:
for(i=0; i < MANT_NUM; i++)
mant[i] /= 2;
//Второй проход:
for(i=MANT_NUM-1; i >=0 ; i--)
if(dx/mant[i] >= N )
break;
if(i >= 0)
break;//Все хорошо, N меток будет.
//Умножаем метки на 5:
for(i=0; i < MANT_NUM; i++)
mant[i] /= 5;
}
//2)Ищем наименьшую разницу между требуемым количеством меток и
//тем количеством меток, которое можно получить:
int min_diff = (int)(dx/mant[0]), md_ind = 0;
for(i = 0; i < MANT_NUM; i++)
{ int diff = abs((int)(dx*1.00000001/mant[i])-N);
if(diff < min_diff)
{ min_diff = diff;
md_ind = i;
}
}
//Рассчитываем значения меток:
double dm = mant[md_ind];
double start = dm*(int)(xmin*1.00000001/dm);
if(start < xmin)
start += dm;
static TDoubleArray values;
values.resize(1);
values[0] = start;
values.reserve(N+1);
while(start+dm < xmax)
{ start += dm;
values.push_back(start);
}
return values;
#undef MANT_NUM
}

4

SmartPointers для C++ в стиле Java

Не так давно я перешел с плюсов на Java. Согласен с автором поста в том, что чисто по-человечески писать на Java приятней, чем на C++. Страхов меньше, язык проще, в стандартной библиотеке есть почти все, что нужно. Одно мне не нравится - все же для любой проги на Java необходимо окружение, а 17 МБ (для JRE 5) не каждый готов скачать ради того, чтобы запустить простенькую програмку. Хочется мне иметь в C++ безопасность и удобство Java.
В процессе мозголомства и раздумий на тему - как же в принципе реализуется сборщик мусора( он же garbage collector - GC) и лазанью по инету пришел к одному результату... А именно: в C++ можно создать SmartPointer, пользоваться которым можно абсолютно так же, как в Java пользуются ссылками на объекты. И поведение его будет таким же, как поведение ссылок в Java. При этом GC как таковой даже и не нужен - его функции будет выполнять сам SmartPointer.

Как ведут себя ссылки в Java? Через них можно обращаться к полям/методам класса, присваивать ссылкам другие ссылки совместимых типов, выполнять проверку на равенство, делать ссылку "инвалидной" (т.е. присвоить ей значение null). По выходе ссылки из области видимости память, связанная с этой сылкой, может быть освобождена сборщиком мусора, при условии что эта ссылка не была передана куда-то еще. Вот, пожалуй и все.
Итак, если в нашей программе будут только такие SmartPointer-ы и не будет других видов указателей, про ручное освобождение памяти и утечку памяти можно будет забыть. Конечно, можно умудрится сделать циклические ссылки (и на Java это сделать проще, чем на C++) В таких случаях, конечно, придется освобождать память вручную, но не через delete, а просто присваивая ссылке значение NULL, что гораздо безопаснее (можно присваивать одной и той же ссылке NULL сколько влезет, и не будет ничего страшного).
Что бы быть подобием java-ссылки, наш SmartPointer должен уметь:
-выполнять операцию присваивания таких же SmartPointer-ов или SmartPointer-ов совместимых типов,
-выполнять операцию присваивания значения NULL,
-выполнять операцию сравнения.
-знать, сколько его копий существует на данный момент.
При всем этом не должно происходить утечек памяти. То есть если один указатель перезаписывается другим, то память на которую указывал "затертый" указатель, должна быть освобождена.
Объявление этого SmartPointer-a я изобразил так:
template<class T>
class AkSmartPointer{
private:
int *ptrCount;
T* ptrObject;
//Удаление 1 экземпляра ССЫЛКИ:
void removeRef();
public:
//Конструкторы:
//"По умолчанию":
AkSmartPointer();
//При создании экземпляра объекта:
AkSmartPointer(T *object);
//Копирование:
AkSmartPointer(const AkSmartPointer<T> &sp);
//Оператор присваивания:
AkSmartPointer<T> & operator=(const AkSmartPointer<T> &s;sp);
//Оператор проверки на равенство:
bool operator == (const AkSmartPointer<T> &sp) const;
//Удаление 1 экземпляра ССЫЛКИ:
~AkSmartPointer();
//Поучение ссылки на объект:
T * operator ->();
};
Вроде ничего не забыл :) Получение указателя необходимо для доступа к интерфейсу указуемого объекта. Реализацию такого SmartPointer-а я сделал вот такой:

class AkSmartPointerException{
public:
AkSmartPointerException( const char *msg, const char *fname, int line){
printf("Error: %s at %s:%i\n", msg, fname, line);
}
};
template<class T>
class AkSmartPointer{
private:
int *ptrCount;
T* ptrObject;
public:
//Конструкторы:
//"По умолчанию" просто обнуляем все указатели:
AkSmartPointer(){
ptrCount = NULL;
ptrObject = NULL;
}
//При создании экземпляра объекта:
AkSmartPointer(T *object){
ptrObject = object;
if(ptrObject != NULL){
ptrCount = new int(1);
printf("New reference 0x%X created.\n", (unsigned)ptrObject);
}else
ptrCount = NULL;
}
//Копирование:
AkSmartPointer(const AkSmartPointer<T> &sp){
ptrCount = sp.ptrCount;
ptrObject = sp.ptrObject;
if(ptrCount != NULL){
(*ptrCount)++;
printf("New copy of reference 0x%X created, %i references total\n",
(unsigned)ptrObject, *ptrCount);
}
}
//Оператор присваивания:
AkSmartPointer<T> & operator=(const AkSmartPointer<T> &sp){
//Если данный объект уже равен присваемому:
if(sp.ptrCount == ptrCount && sp.ptrObject == ptrObject)
return *this;// ничего не делаем, просто возвращаем текущее значение.
//если выполняется только одно условие из двух - то происходит нечто странное,
//и мы выкидываем исключение:
else if(sp.ptrCount == ptrCount || sp.ptrObject == ptrObject)
throw AkSmartPointerException("ptrCount or ptrObject is equal in different smart pointers", __FILE__, __LINE__);
removeRef();//удаляем у текущего объекта ссылку
ptrCount = sp.ptrCount;
if(ptrCount != NULL)
(*ptrCount)++;
ptrObject = sp.ptrObject;
if(ptrObject != NULL && ptrCount != NULL)
printf("New copy of reference 0x%X created, %i references total\n",
(unsigned)ptrObject, *ptrCount);
return *this;
}
//Оператор проверки на равенство:
bool operator == (const AkSmartPointer<T> &sp) const{ return ptrObject == sp.ptrObject; }
//Удаление 1 экземпляра ССЫЛКИ:
~AkSmartPointer(){removeRef(); }
//Получение ссылки на объект:
T * operator ->(){
if(ptrCount != NULL)
return ptrObject;
throw AkSmartPointerException("Null pointer dereferencing", __FILE__, __LINE__);
}
//Удаление 1 экземпляра ССЫЛКИ:
private:
void removeRef(){
if(ptrCount != NULL){
(*ptrCount)--; //Если ссылок не осталось - зануляем все остальное:
if(*ptrCount <= 0){
unsigned x = (unsigned)ptrObject;
delete ptrCount;
if(ptrObject != NULL)
delete ptrObject;
printf("All references 0x%X deleted.\n", x);
}else
printf("One reference 0x%X removed, %i remaining\n",
(unsigned)ptrObject, *ptrCount);
ptrCount = NULL;
ptrObject = NULL;
}
else if(ptrObject != NULL)//если ptrCount == NULL, но ptrObject != NULL
throw AkSmartPointerException("ptrCount == NULL, but ptrObject != NULL", __FILE__, __LINE__);
else
printf("Do nothing - ptrCount and ptrObject is null.\n");
}
};
Наверное, все понятно и так, но немножко комментариев напишу. Каждое действие по созданию или удалению ссылок на объект я сопровождаю выводом на экран, дабы было видно, какие функции-члены SmartPointer-а вызываются при манипуляциях с ним, отсюда куча printf().

В операторе присваивания сначала проверяем, не присваиваем ли мы нашей ссылке ее же значение. Если да - число ссылок не изменилось и ничего делать не надо. Если нет (не выполняются оба условия sp.ptrCount == ptrCount и sp.ptrObject == ptrObject, либо лишь одно из них), то проверяем: оба условия не выполняются или только одно из них. Если только одно условие не выполняется-например, sp.ptrCount == ptrCount, но sp.ptrObject != ptrObject, - это значит, что ссылки на разные объекты имеют общий счетчик, а это недопустимо. Таким образом, в случае выполнения только одного условия из двух, сообщаем об ошибке. Если оба условия не выполняются, то все ОК - одна ссылка перезаписывается другой. В результате присваивания одна из ссылок должна быть удалена (та, что стояла в левой части выражения присваивания) и появится еще одна новая ссылка (та, что стояла в правой части этого выражения). Для удаления ссылки вызываем функцию removeRef у объекта-владельца этой ссылки.

Все остальное по-моему достаточно очевидно (если честно, упарился описывать :) ). Эту реализацию SmartPointer я разместил в файле refCount.hpp.

Уфф, добрались до самого интересного (говорят, русским программистам всегда не терпится запустить программу и увидеть ее в работе. Это правда :) ). Написал я тест и запустил, скрестив пальцы под столом. Сначала хотел его целиком тут привести, но потом передумал. Тест просто показал, что такой SmartPointer вроде бы работает именно так, как я ожидал. Приведу здесь только ту часть теста, которая показала, что происходит при создании объектов и как работает vector.
#include <vector>
#include "refCount.hpp"
struct TestObject{
double x, y;
TestObject(double _x, double _y): x(_x), y(_y){}
};
using namespace std;
typedef AkSmartPointer<TestObject> TestObjectSmartPtr;
int main(int n, char **args){
printf("Declare vector<TestObjectSmartPtr>:\n");
vector<TestObjectSmartPtr> refvect;
{ //Начало блока.
printf("Declare objects a, b, c:\n");
AkSmartPointer<TestObject> sp_a, sp_b, sp_c;
printf("Create object a:\n");
sp_a = new TestObject(1.0, 1.0);//всего 1 ссылка на a
.....
} //Конец блока.
Этот участок кода дал мне такую вот распечатку:
Declare vector:
Declare objects a, b, c:
Create object a:
New reference 0x3F3C90 created.
Do nothing - ptrCount and ptrObject is null.
New copy of reference 0x3F3C90 created, 2 references total
One reference 0x3F3C90 removed, 1 remaining
Хорошо видно, какую последовательность вызовов порождает выражение

sp_a = new TestObject(1.0, 1.0);//всего 1 ссылка на a
Расписывать как все происходит, не буду, дабы не утомлять читателей. :)

Теперь - о том,что касается работы vector. Свой тест я откомпилил в MinGW версии 3.4.2, в Borland C++ builder 5 и в MSVC 6.0 c подключенным компилятором IA-32 v5.0. Что касается создания объектов, и в том и в другом случае распечатки были почти идентичны (за исключением значений адресов объектов). Но что касается vector - разница заметна. В моем тесте есть такой кусок:

printf("Addition object a:\n");
refvect.push_back(sp_a);//2 ссылки
printf("Addition object b:\n");
refvect.push_back(sp_b);//2 ссылки
printf("Addition object c:\n");
refvect.push_back(sp_c);//2 ссылки
Откомпилированный под MinGW, этот кусок вывел на экран следующее:
Addition object a:
New copy of reference 0x3F3C90 created, 2 references total
Addition object b:
New copy of reference 0x3F3C90 created, 3 references total
New copy of reference 0x3F23B8 created, 2 references total
One reference 0x3F3C90 removed, 2 remaining
Addition object c:
New copy of reference 0x3F3C90 created, 3 references total
New copy of reference 0x3F23B8 created, 3 references total
New copy of reference 0x3F23E8 created, 2 references total
One reference 0x3F3C90 removed, 2 remaining
One reference 0x3F23B8 removed, 2 remaining

То есть каждый раз при добавлении 1 элемента в конец вектора, под весь вектор выделялся новый участок памяти, туда копировались элементы из "старого" вектора, а старый участок памяти удалялся(!). То же самое в версии STL MSVC 6.0 (но это ладно, она очень древняя). Похоже, в Borland-е все сделано куда разумнее. Откомпилированный в Borland-е тот же кусок программы вывел на экран гораздо меньше сообщений:
Addition object a:
New copy of reference 0x65415C created, 2 references total
Addition object b:
New copy of reference 0x654180 created, 2 references total
Addition object c:
New copy of reference 0x6541A4 created, 2 references total

Как видно, реализация vector от Borland все же лучше аналогичной в MinGW. Похоже, у Borlan-да при создании вектора сразу резервируется блок под несколько элементов.
Ну и под конец - один из способов, как приведенную реализацию SmartPointer-а заставить работать неправильно:
AkSmartPointer a = new Object(...);
Object *b = a.operator->();
delete b;//пипец!

Но имхо, это будет уже маразм ради маразма :) Если знаете, как с таким SmartPointer-ом организовать утечку памяти - плиз, кидайте в комменты примерчик, но только без описанного маразма или ему подобных (желательно программу целиком). Циклические ссылки приветствуются, но желательно, что бы Ваш пример был компилируемым и запускаемым :). К моему стыду, я до сих пор не знаю, как циклические ссылки можно организовать в C++ (мне один раз это было надо, но я их реализовал так криво, что тошно вспоминать).
P.S. Мне сложно было реализовать ситуацию, когда 2 объекта разных типов должны были ссылаться друг на друга.
5

Воспоминания о лете...

Минувшим летом мы с мужем отдыхали на море. 10 дней в Лазаревском: море, горы, солнце и отсутвие компьютера... Вот это отдых для жены программиста, ну, и разумеется, для самого программиста:)

Сейчас, когда на улице всего +5° С, а батареи греют лишь в одной комнате из трех, воспоминания о летней поездке согревают, если не ноги, то хотя бы душу:))

1

Мой словарный запас

Воть, тест прошел. На проверку словарного запаса :)
Итак, ваш запас: Ваш словарный запас просто великолепен, вероятно вы профессионально владеете языком и решаете кроссворды с легкостью!
Пройти тест
1

Можно ли сделать C++ безопаснее?

Конечно, я наверное, таким вопросом слишком на многое замахнулся. Пусть не "безопасным", но хотя бы чуть менее "опасным" в том, что касается работы с динамической памятью. Я считаю C++ "опасным" языком в этом отношении и думаю, что многие со мной согласятся. С какими основными проблемами встречается человек, пишущий на C++? Думаю, не сильно ошибусь, если скажу, что это утечки памяти и выход за границы диапазонов динамических массивов. С первой проблемой дело обстоит очень сложно, зато незначительные утечки памяти редко вызывают крах программы, если она не работает круглосуточно (например, некая игра для PC, текстовый процессор или какой-нибудь Computer-Aided Design). А вот выход за границы порой валит прогу в самом интересном месте. Даже если вы используете только safe arrays со всяческими проверками и генерацией исключений - все равно бывает, что не один час уйдет на поиск места, где произошла ошибка.
Признатся, я перешел с C++ на Java во многом именно из-за проблем с памятью. Но, кроме сборщика мусора, мне в Java импонирует еще одна вещь: в случае возникновения исключения во время исполнения программы, Java распечатывает стек вызовов! Устраняя глюки (те, что вызывают исключения) в программах на Java, я даже отладчик не использовал, и долгое время (первый год) не пользовался им вообще. После C++ это было что-то!!! Сообщения об ошибках в C++ которые выплевывались при генерации исключений и распечатка содержимого стека (одни цифири) не давали непосвященному вроде меня почти никакой информации о том, где произошла ошибка. Особенно, если использовались шаблоны, а ошибка происходила где-нибудь в debug/vector. В этой статейке я расскажу о своих попытках распечатки стека вызовов в C++ (в случае генерации исключения), в таком же виде, в каком я привык это видеть в Java. Специально для этой этого я написал маленькую программку (надеюсь, моя статья не такая паршивая, как эта программа :)))).

#include <stdio.h>
#include <vector>
void fillDoubleVector(vector<double> &vect){
vect.resize(20);
unsigned size = vect.size();
for(int i=size-1; i >= 0; i++)//Ошибка
vect[i] = i*(double)size - size/(double)(i+1);
}
//-----------------------------------------------------
void fillLongVector(vector<long> &vect){
vector<double> dvect;
fillDoubleVector(dvect);
unsigned size = dvect.size();
vect.resize(size);
for(int i=size-1; i >= 0; i++)//Ошибка
vect[i] = i*size - size/(i+1);
}
//-----------------------------------------------------
int main(int n, const char **args){
vector<long> lvect;
fillLongVector(lvect);
for(int i=0; i < lvect.size(); i++)
printf("lvect[%i] = %i\n", i, lvect[i]);
return 0;
}

Программу сохранил в файле test.cpp откомпилил в MinGW:

g++ -o test.exe -D _GLIBCXX_DEBUG -g3 test.cpp

Определил _GLIBCXX_DEBUG , что бы в программе использовалась отладочная версия STL (которая хотя бы сообщает о таких ошибках, как выход за пределы диапазона). Получил вот такое сообщение об ошибке:

C:/DEV-CPP/BIN/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/
debug/vector:192:
error: attempt to subscript container with out-of-bounds index 20,
but container only holds 20 elements.

Objects involved in the operation:
sequence "this" @ 0x0074FDC0 {
type = N15__gnu_debug_def6vectorIdSaIdEEE;
}
abnormal program termination
Замечательное и очень информативное сообщение об ошибке! Только местоположение ошибки указано весьма туманно. Было бы еще змечательно, что бы после этого еще и стек вызовов распечатывался. Переделал я эту программулину, вставив в начало и конец каждой функции блоки try-catch:

#include <stdio.h>
#include <exception>
using namespace std;
#include <vector>
//Класс-исключение:
class MyException : public exception
{
public:
MyException(const char *msg, int line)
{
printf("Error in file %s : %i\n", msg, line);
}
};
//--------------------------------------------------
void fillDoubleVector(vector<double> &vect){
try{
vect.resize(20);
unsigned size = vect.size();
for(int i=size-1; i >= 0; i++)//Ошибка
vect[i] = i*(double)size - size/(double)(i+1);
}catch(exception ex){
throw MyException(__FILE__, __LINE__);
}
}
//-----------------------------------------------------
void fillLongVector(vector<long> &vect){
try{
vector<double> dvect;
fillDoubleVector(dvect);
unsigned size = dvect.size();
vect.resize(size);
for(int i=size-1; i >= 0; i++)//Ошибка
vect[i] = i*size - size/(i+1);
}catch(exception ex){
throw MyException( __FILE__, __LINE__);
}
}
int main(int n, const char **args){
try{
vector<long> lvect;
fillLongVector(lvect);
for(int i=0; i < lvect.size(); i++)
printf("lvect[%i] = %i\n", i, lvect.at(i));
}catch(exception ex){
printf("Error in file %s : %i\n", __FILE__, __LINE__);
}
return 0;
}

Откомпилил, запустил - млин, результат нисколько не изменился!!! Чертовщина, блин! Похоже, что в vector при выходе за пределы массива не выбрасывается исключение, во всяком случае в реализации MinGW. Полез в заголовочные файлы (исходников всей библиотеки, к сожалению, не было) - так и есть, не выбрасывается даже в отладочной версии.
Потом уже прочел в описании STL, что оператор индексирования [] исключение не выбрасывает и проверок никаких не делает, но зато есть функция-член at(), которая работает точно так же как оператор [], но проверки делает и исключение выбрасывает.
Отладочную версию файла vector я несколько раскурочил (перекрестившись и сохранив его копию), вставив в тело функции operator [] вызов функции at() и добился-таки желаемого:

Error in file test2.cpp : 23
Error in file test2.cpp : 36
Error in file test2.cpp : 47

Жаль, что старое информативное сообщение об ошибке уже не выводится. Но все можно исправить, для этого надо немного поковырять исходники библиотек(насколько я понимаю - вставить вместо вызова функции abort() генерацию исключения). Найти бы тока время, что бы этим заняться, и трафик, что бы их (stdlibc++ sources) скачать :)
Еще в дополнение замечу, что кроме выходов за границы есть и другие ошибки, которые можно было бы так отлавливать - главное, что бы при их возникновении выкидывалось исключение. Конечно, ручками вставлять кучу блоков try/catch вряд ли кто захочет, да и вряд ли это нужно делать ручками. Но вот если написать такую приблуду, которая будет сама это в исх. код вставлять, потом все это компилить, а потом из исходного кода удалять... Напишите для меня такую, кто-нибудь, заплатить не обещаю, но обещаю 100 раз скачать :)
0

Эмуляция действий пользователя в Java

Пишу заготовку для графического редактора на Java. Захотелось мне, что бы в моей проге было как в P-CAD: при изменении масштаба указатель мыши всегда указывал на одну и ту же точку изображения. В P-CAD для пущей крутизны эта точка еще и переносится в центр окна. Сначала все сделал без перемещения указателя к центру. Вроде ничего, приятно :) Потом захотелось крутизны. Но вот незадача - понятия не имею, как в Java указатель мышки программно переместить. Пробовал генерировать события для окна через dispatchEvent() - ни хрена не помогает. После получаса поисков с грехом пополам на форуме Винграда нашел подсказку - оказывается, для этих целей аж специальный класс есть!!! Под названием java.awt.Robot.
Перемещение указателя мышки в нужную точку компонента оказалось очень простым:

import java.awt.*;
...
try{
Robot rob = new Robot();
Point2D p1 = comp.getLocationOnScreen();
rob.mouseMove((int)p1.getX() + comp.getWidth()/2,
(int)p1.getY() + comp.getHeight()/2);
}
catch(AWTException ex){
System.out.println("Can't move mouse to center, error in DrawingWindow.java:195");
}

Здесь у меня comp - экземпляр класса, наследованного от класса java.awt.Panel.
0

Техномаразматический сон

Да простит меня автор блога Уголок техномаразма за заимствование меткого термина. :) Просто не получается никак иначе назвать ахинею, приснившуюся мне сегодня ночью.
Сюжет:
Едем мы с моей женой куда-то на междугородном автобусе. Все замечательно, в окошко глядим. Потом после остановки мы почему-то оказываемся сидящими у двери (но не у той, что впереди, а той, которая у некоторых автобусов есть в середине). Чтой-то я стал этой дверью баловаться, и она стала на ходу открываться, причем как-то странно - навстречу потоку воздуха (т.е. при движении автобуса поток воздуха стремится распахнуть дверь, а не препятствует этому -так на старых легковых автомобилях делали передние дверцы). Я ее закрою - она опять открывается... Водила терпел, потом остановил автобус, и что-то с этой дверью сделал, и мы опять поехали. Тут я гляжу - дверь стала надуваться(!) Ну, я говорю супруге - смотри, мол, он режим надувания двери включил, что бы она в дверном проеме застряла и не открывалась(!) (Счас пишу эти строки и охреневаю, как же мне такое только в голову пришло). Потом эта дверь опять стала сдуваться. Я говорю жене - ну, похоже проблемы, прокол в двери, придется ремонтировать (!)
Следующая картина - забираем с женой из салона катушку ниток и иголку. Потом почему-то оказываемся в каком-то садовом домике рядом со столом, на котором разложено куча инструментов и лежит сдувшаяся дверь(!) Водитель стоит напротив меня за столом и говорит - мол, дыра в двери, надо зашивать, требуется нитка и иголка (!). Предлагает свою иголку - она какая-то гигантская, сантиметров 10 длиной и загнутым кончиком. Мне она не нравится, я ищу ту иголку, что мы подобрали в салоне, иголку нахожу, а нитку - нет, где-то эта нитка под кучей инструментов завалялась.
Занавес.
Обычно сны у меня сразу забываются, а этот почему-то сохранился в памяти. Блин, и ведь накануне не напивался - только персики ел... и арбуз немного...
3

Шаблоны в Java

В Java 5 наконец-то появились шаблоны. После C++ без них как-то совсем тяжко :) Эмулировать шаблоны, создавая все структуры данных (т.е. классы) для базового типа object - имхо, идея не очень хорошая.

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

class foo<T>
{
...
T data[] = new T[n];
...
}

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

Например, можно написать на Java код для шаблона динамического массива. Правда, в конструктор массива придется передать все же ссылку на один инициализированный объект нужного или производного от него класса.
Реализация вот такая:

package examples;
import java.lang.*;
import java.lang.reflect.*;

public class ExampleGen<T1>
{
T1 dim[] = null;
Class cl = null;
//Конструктор. передаем в кач-ве параметра инициализированную ссылку на объект
//нужного (или производного от него) типа
public ExampleGen(T1 var)
{
cl = var.getClass();
}
public void setSize(int n)
{
T1 tmp[] = (T1 [])Array.newInstance(cl, n);
if(dim != null)
for(int i=0; i < dim.length && i < n; i++)
tmp[i] = dim[i];
dim = tmp;
}
public int getSize()
{
return dim.length;
}
public T1 get(int n)
{
return dim[n];
}
public void set(int n, T1 value)
{
dim[n] = value;
}
public T1 [] getArray()
{
T1 tmp[] = (T1 [])Array.newInstance(cl, dim.length);
if(dim != null)
for(int i=0; i < dim.length; i++)
tmp[i] = dim[i];
return tmp;
}
public static void main(String [] args)
{
ExampleGen data;
data = new ExampleGen<T1>( new Double(1.0) );
data.setSize(20);
for(int i=0; i < data.getSize(); i++)
data.set(i, new Double(Math.cos((double)i+1)) );
System.out.println("Values of structure:");
for(int i=0; i < data.getSize(); i++)
System.out.println("Value #" + i + " = " + data.get(i));

Double a[] = data.getArray();
System.out.println("\nValues of array:");
for(int i=0; i < data.getSize(); i++)
System.out.println("Value #" + i + " = " + a[i]);
}
}

Все работает. Инициализированная ссылка нужна лишь для того, что бы получить описание класса - заполнить поле cl.
0

Java-процессор на FPGA

Недели 2 назад пришла идея, что можно было бы такую штуку сделать. Прогуглил - оказалось такая идея людям приходила еще в 1999 г, когда я о Java только краем уха слышал. Казалось бы, перспективная штука, но большинство найденных мной документов по данной теме датированы 2004 г. или раньше. Единственное более позднее сообщение, которое я смог найти - A methodology for Java/FPGA co-design Это только автореферат... Хотелось бы почитать саму работу, тока пока не понятно, как с авторами связаться. Судя по тому, что в автореферате написано, в создании "java-процессора" имеются существенные успехи.
0

GUI front-end для GDB

Наверняка все, кто используют компиляторы из пакета MinGW слышали про такую штуку, как GDB (GNU Debugger). Весчь полезная, но на мой взгляд - шибко навороченная и неудобная. Привык при отладке видеть исходный текст, причем лучше - всех файлов проекта, а не только первого. Поиски нормальной оболочки для отладчика окончились ничем. Более-менее неплохая оболочка для GDB была в составе Gygwin-a, но... Скачивать весь Cygwin только из-за одной этой оболочки - мутило, на диалапе-то. Вот поэтому я и начал в 2006 году писать графическую оболочку для GDB под названием GDBShell. Выполняются простейшие операции: установка/удаление точек останова, выполнение программы до позиции курсора, пошаговое выполнение, пошаговое выполнение с заходом в блоки, можно посмотреть/изменить значение переменных.
Страничка, посвященная GDB.
Последнюю версию (которую я разместил только вчера), с исправленными глюками, можно скачать тут:
Download GDBShell: GDB GUI front-end
Скриншоты прилагаются :)))))

Главное окно и меню отладки:

Всплывающая менюшка (по-моему, должно быть довольно удобно)

Окно просмотра/изменения значений переменных:
0

Л.И. Брежнев о мировом финансовом кризисе

4

Перенаправление ввода/вывода в Java

Воть и сам муж появился... :))) Пока попробую опубликовать те статейки, котрые писал на vingrad.ru
21.01.08
9.28
Да-а-а-а... "совсем ерунда - перенаправить вывод программ куда-нибудь...". Все оказалось не так просто, как я думал. В тех книжках, где я имел счастье видеть примеры запуска внешних программ либо не было сказано, как это делать вообще, либо описано это очень неточно и неполно. К такому выводу я пришел, когда у меня наконец-то все заработало.

Итак, сперва я решил просто запустить внешнюю программу, но не notepad, как в большинстве примеров в книгах по java (на мой взгляд, более бесполезного примера трудно придумать), а внешнюю java-программу.Строка запуска должна быть вот такой:
java -cp . -ea HelloWorld
где программка HelloWorld выводит всем известную со времен Кернигана и Ричи фразу :). Сам текст внешней программки:
import java.lang.*;
public class HelloWorld
{
static public void main(String [] args)
{
System.out.println("Hello, world!");
System.exit(2);
}
}
Дело видете ли в том, что мне помимо запуска внешней программы надо получить код, возвращаемый программой, и если он отличается от 0 (т.е. произошла ошибка), вывести сообщение, которое вывела внешняя программа. В данном случае возвращаю 2, что бы было понятно что получившееся значение - результат работы именно этой программы, а не что-либо еще (например, код ошибки, возвращаемый java-интерпретатором). Вот мое первое творение (весьма далекое от того, что должно быть, поскольку в кач-ве образца использовался простейший пример с notepad-ом из книги "Полный справочник по Java" Г.Шилдта):
import java.lang.*;
public class RunHello
{
static public void main(String [] args)
{
String cmdline[] = {"java", "-cp", "C:\\Java\\Projects\\hello", "HelloWorld"};
Runtime rt = Runtime.getRuntime();
Process p = null;
try{
p = rt.exec(cmdline);
int res = p.waitFor();
System.out.println("Exit code = " + res);
}catch(Exception e)
{
System.out.println("Runtime error with message: " + e);
}
}
}

Запускаем RunHello и пока все отлично - программа работает, только сообщение "Hello, world!" не выводится.
Теперь немножко изменим программку HelloWorld:
import java.lang.*;
public class HelloWorld
{
static public void main(String [] args)
{
for (int i = 1; i <= 40; i++)
System.out.println("Hello, world! " + i);
System.exit(2);
}
}

Теперь сообщение должно вывестись 40 раз подряд, вместе с порядковым номером вывода. Запускаем RunHello и.. ничего не происходит - программа благополучно повисает. Дабы этого не происходило, нужно читать вывод программы. То есть нужно читать из потоков, возвращаемых методами getInputStream() и getErrorStream(), определенных в классе Process.
Модифицируем нашу программку RunHello:
import java.lang.*;
import java.io.*;
public class RunHello
{
static public void main(String [] args)
{
String cmdline[] = {"java", "-cp", "C:\\Java\\Projects\\hello" , "HelloWorld"};
Runtime rt = Runtime.getRuntime();
Process p = null;
try{
p = rt.exec(cmdline);
BufferedReader stdinput = new BufferedReader(new InputStreamReader(p.getInputStream()));
int m;
do{
m = stdinput.read();
System.out.print((char)m);
}while(m >= 0);
int res = p.waitFor();
System.out.println("Exit code = " + res);
}catch(Exception e)
{
System.out.println("Runtime error with message: " + e);
}
}
}
Вывод дочернего процесса естественно, не обязательно печатать. Все замечательно работает, и нет проблем. Но пока рано радоваться :) Теперь немножко модифицируем программу HelloWorld. Помимо стандартного устройства вывода будем печатать еще и на стандартное устройство ошибок.
import java.lang.*;
public class HelloWorld
{
static public void main(String [] args)
{
for (int i = 1; i <= 40; i++)
{
System.out.println("Hello, world! " + i);
if(i < 30)
System.err.println("It's prints to stderr" + i);
}
System.exit(2);
}
}
Теперь наша программа выведет некоторое число сообщений "Hello, world! " и встанет, поскольку мы в програме RunHello не читаем из стандартного устройства ошибок. Что ж, надо читать :)
import java.lang.*;
import java.io.*;
public class RunHello
{
static public void main(String [] args)
{
String cmdline[] = {"java", "-cp", "C:\\Java\\Projects\\hello" , "HelloWorld"};
Runtime rt = Runtime.getRuntime();
Process p = null;
try{
p = rt.exec(cmdline);
BufferedReader stdinput = new BufferedReader(new InputStreamReader(p.getInputStream())),
stderror = new BufferedReader(new InputStreamReader(p.getErrorStream()));
int m1, m2;
do{
m1 = stdinput.read();
if(m1 > 0)
System.out.print((char)m1);
m2 = stderror.read();
}while((m1 >= 0) || (m2 >= 0));
int res = p.waitFor();
System.out.println("Exit code = " + res);
}catch(Exception e)
{
System.out.println("Runtime error with message: " + e);
}
}
}
Пока у меня на Win98 все отлично работает. Попробуем в программе HelloWorld количество циклов увеличить с 40 до 80:
for (int i = 1; i <= 80; i++)
{
System.out.println("Hello, world! " + i);
if(i < 30)
System.err.println("It's prints to stderr" + i);
}
Как видим, приключения еще не кончились! :) Опять программа подвисает, у меня - после вывода 40-го сообщения. Если честно, пока я проходил все эти стадии прозрения, мой стол немного пострадал, поскольку я лупил по нему кулаком от злости :) Так же немного пострадал мой кулак :).
Но все же есть выход из положения, позволяющий запускать любые внешние программы, что угодно печатающие на консоль и в стандартное устройство ошибок. Прежде чем читать что-то из потоков stderror или stdinput, необходимо проверить, можно ли производить чтение и не приведет ли это к блокировке процессов. Это можно сделать, вызывая для потоков метод ready(). Он возвращает true(истину), если читать из потока можно. Итак, окончательная версия программки RunHello у меня получилась такой:
import java.lang.*;
import java.io.*;
public class RunHello
{
static public void main(String [] args)
{
String cmdline[] = {"java", "-cp", "C:\\Projects\\Java\\hello" , "HelloWorld"};
Runtime rt = Runtime.getRuntime();
Process p = null;
try{
p = rt.exec(cmdline);
BufferedReader stdinput = new BufferedReader(new InputStreamReader(p.getInputStream())),
stderror = new BufferedReader(new InputStreamReader(p.getErrorStream()));
int res=0;
int m1;
do
{
if( stderror.ready() || stdinput.ready())
{
if(stdinput.ready())
{
m1 = stdinput.read();
if(m1 >= 0)
System.out.print( (char)m1 );
}
if(stderror.ready())
stderror.read();
}
else
try{
res = p.exitValue();
break;
}catch(java.lang.IllegalThreadStateException ex){}
}while(true);
System.out.println("Exit code = " + res);
}catch(Exception e)
{
System.out.println("Runtime error with message: " + e);
}
}
}

В цикле сначала проверяем, готов ли какой-либо поток к чтению из него. Если оба потока не готовы - есть вероятность, что программа закончила свою работу (но это еще не факт!! Как корректно проверить, завершил процесс работу или нет, я так и не нашел в документации на Java API. Если кто знает - напишите, буду весьма признателен! :) ) После пробуем получить значение, возвращаемое внешней программой. Если внешняя программа завершила работу - завершаем цикл и печатаем возвращаемый код.