Архив рубрики: Java

Установка в Linux Mint 18.3 различных инструментов, используемых для программирования

Мне не нравится современный пользовательский интерфейс Linux Ubuntu, но в то же время мне нравится более классический интерфейс Linux Mint 18.3, построенной (согласно официальной информации) на базе Ubuntu 16.04. Поэтому на моём ноутбуке установлен Linux Mint 18.3.

Поскольку меня интересует программирование на языках C, C#, JavaScript и Java, то в данной заметке я размещаю краткую шпаргалку о том, какой софт можно установить в Linux Mint 18.3 для возможности разработки софта с использованием упомянутых выше языков программирования.

GCC

В качестве компилятора для языка C я конечно же предпочитаю использовать gcc. К сожалению, по умолчанию, в Linux Mint 18.3 установлена весьма старая (5-я) версия этого компилятора. Информацию о версии установленной у вас версии gcc всегда можно получить так:

gcc --version

Инструкцию о том, как можно обновить gcc можно найти здесь. Последовательно выполнив все обозначенные в ней действия, мне без труда удалось успешно обновить gcc до наиболее свежей на сегодняшний день версии (7-й).

На всякий случай дублирую содержимое ссылки, дабы в случае удаления кем-либо указанной выше информации она бы не была безвозвратно утеряна:

These commands are based on a askubuntu answer http://askubuntu.com/a/581497 and https://askubuntu.com/questions/26498/choose-gcc-and-g-version
To install gcc-7 (gcc-7.2.0), I had to do more stuff as shown below.
USE THOSE COMMANDS AT YOUR OWN RISK. I SHALL NOT BE RESPONSIBLE FOR ANYTHING.
ABSOLUTELY NO WARRANTY.

If you are still reading let's carry on with the code.

sudo apt-get update && \
sudo apt-get install build-essential software-properties-common -y && \
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
sudo apt-get update && \
sudo apt-get install gcc-snapshot -y && \
sudo apt-get update && \
sudo apt-get install gcc-7 g++-7 gcc-6 g++-6 gcc-multilib -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-7 && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6 && \
sudo apt-get install gcc-5 g++-5 -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 50 --slave /usr/bin/g++ g++ /usr/bin/g++-5;

When completed, you must change to the gcc you want to work with by default. Type in your terminal:
sudo update-alternatives --config gcc

To verify if it worked. Just type in your terminal
gcc -v

If everything went fine you should see gcc 7.2.0 by the time I am writing this gist

Happy coding!

See blog post at https://www.application2000.com

Помимо того, что gcc можно использовать непосредственно из консоли, его так же используют и различного рода IDE, например, используемый мною CLion компании JetBrains. Эта IDE позволяет писать на C90, С99 и даже С11, в отличии от  той жеVisual Studio, позволяющей писать только на  C90. 

VIM & IDE

В качестве текстового редактора я предпочитаю vim. Это приложение особенно полезно для тех, кто владеет слепой десятипальцевой печатью, которой можно обучиться, например, на сайте Владимира Шахиджаняна:  https://solo.nabiraem.ru/ - свои навыки я когда-то получил именно там.

Компания JetBrains предоставляет набор кроссплатформенных IDE для интересующих меня языков программирования:
Для каждого из этих редакторов присутствует возможность установить плагин IdeaVim, позволяющий эмулировать vim в качестве текстового редактора IDE. Т.о. навыки использования vim могут быть успешно использованы в используемых мною IDE.

Установка vim:

sudo apt-get install vim

Проверка установленной версии vim:

vim --version

Краткая обучающая инструкция по базовым основам использования vim:

vimtutor

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

sudo tar -xzf your.tar.gz -C /opt

В результате приложение будет установлено в каталог /opt.

Visual Studio Code

На официальном сайте Майкрософт можно скачать и установить самый свежий дистрибутив данного кроссплатформенного текстового редактора.

Среди доступных плагинов для этого текстового редактора так же имеется большой набор эмуляторов vim.

Atom

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

Для него так же можно скачать плагин эмуляции vim, как описано здесь:

apm install vim-mode-plus

Git

Вряд ли эта программа нуждается в представлении. На официальном сайте всегда можно скачать и установить самый свежий дистрибутив программы.

JDK

SDK для Java. На официальном сайте можно скачать и установить самую свежую версию JDK, необходимую для разработки кода на Java. Установленный JDK будет использоваться в IntelliJ Idea.

Node.js

Платформа, предоставляющая возможность разрабатывать приложения на JavaScript. С официального сайта устанавливать лучше LTS-версию. При установке Node.js автоматически будет установлен и менеджер пакетов NPM

NPM можно использовать не только для JavaScript, но и в проектах ASP.NET Core MVC 2.

.NET Core SDK

На официальном сайте Майкрософт присутствует подробная инструкция по установке самой свежей версии .NET Core SDK для Linux Ubuntu 16.04, на основе которой создан Linux Mint 18.3.

xUnit

Платформа для разработки модульных тестов для .NET и .NET Core. На официальном сайте даются ссылки на соответствующие NuGet и MyGet пакеты.

Хостинг ASP.NET Core 2 приложений в IIS

На тот случай, если своё web-приложение вы захотите хостить на IIS, компания Майкрософт опубликовала подробную инструкцию по данной теме. Особое внимание следует обратить на то, что на указанной странице, в разделе Установка пакета размещения .NET Core для Windows Server, указан пакет, который необходимо установить на сервере, чтобы IIS научился работать с вашим приложением.

Remmina

Это приложение удобно использовать в качестве RDP-клиента для удалённого подключения к компьютерам, работающим под управлением Windows.

VMWare Horizon Client

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

MS SQL Server 2017

Всю необходимую информацию по теме можно найти на официальном сайте продукта.

PowerShell Core

Наличие возможности использовать PowerShell в Linux является весьма удобной  для тех, кто привык пользоваться этой командной оболочкой в Windows. Например, для тестирования контроллеров API в ASP.NET Core MVC 2 можно воспользоваться привычной командой Invoke-RestMethod.

Инструкция по установке - на официальном сайте здесь.

UPD
Ниже написал небольшой скрипт, с помощью которого оперативно установил интересующий меня набор приложений:

#!/bin/bash
# (c) Андрей Бушман, 2018
# Данный скрипт устанавливает набор необходимого мне программного обеспечения.

sudo apt-get update
sudo apt-get upgrade

# Remmina
sudo apt-add-repository ppa:remmina-ppa-team/remmina-next
sudo apt-get update
sudo apt-get install remmina remmina-plugin-rdp remmina-plugin-secret

# VMware Horizon Client v4.7.0-7395152
wget -O ./vmware.x64.bundle https://download3.vmware.com/software/view/viewclients/CART18FQ4/VMware-Horizon-Client-4.7.0-7395152.x64.bundle
chmod +x ./vmware.x64.bundle
sudo ./vmware.x64.bundle

# Skype
wget -O skype.deb https://go.skype.com/skypeforlinux-64.deb
sudo dpkg --install ./skype.deb

# Git
sudo add-apt-repository ppa:git-core/ppa
sudo apt update; apt install git

git config --global user.name "Andrey Bushman"
git config --global user.email bushman.andrey@gmail.com
git config --global core.editor "vim"

# Google Chrome
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo dpkg -i google-chrome-stable_current_amd64.deb

# GCC v7.3.0
sudo apt-get update && \
sudo apt-get install build-essential software-properties-common -y && \
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
sudo apt-get update && \
sudo apt-get install gcc-snapshot -y && \
sudo apt-get update && \
sudo apt-get install gcc-7 g++-7 gcc-6 g++-6 gcc-multilib -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-7 && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6 && \
sudo apt-get install gcc-5 g++-5 -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 50 --slave /usr/bin/g++ g++ /usr/bin/g++-5;
sudo update-alternatives --config gcc

# VIM
git clone https://github.com/vim/vim.git
cd vim
git pull
cd vim/src
sudo apt-get install libncurses5-dev libncursesw5-dev
# make distclean  # if you build Vim before
make
sudo make install

# .Net Core 2.1.200 SDK
wget -q packages-microsoft-prod.deb https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb

sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install dotnet-sdk-2.1.200

# Visual Studio Code
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list'
sudo apt-get update
sudo apt-get install code # or code-insiders

# Sublime Text 3
wget -qO - https://download.sublimetext.com/sublimehq-pub.gpg | sudo apt-key add -
sudo apt-get install apt-transport-https
echo "deb https://download.sublimetext.com/ apt/stable/" | sudo tee /etc/apt/sources.list.d/sublime-text.list
sudo apt-get update
sudo apt-get install sublime-text


# Atom
curl -L https://packagecloud.io/AtomEditor/atom/gpgkey | sudo apt-key add -
sudo sh -c 'echo "deb [arch=amd64] https://packagecloud.io/AtomEditor/atom/any/ any main" > /etc/apt/sources.list.d/atom.list'
sudo apt-get update
sudo apt-get install atom

# JDK 10.0.1
wget https://download.java.net/java/GA/jdk10/10.0.1/fb4372174a714e6b8c52526dc134031e/10/openjdk-10.0.1_linux-x64_bin.tar.gz
sudo tar xvf openjdk-10.0.1_linux-x64_bin.tar.gz -C /opt

# NodeJS v8.11.1
wget -O node.tar.gz https://nodejs.org/dist/v8.11.1/node-v8.11.1-linux-x64.tar.xz
sudo tar xf ./node.tar.gz -C /opt

# JetBrains Clion 2018.1.2
wget -O ./CLion.tar.gz http://download.jetbrains.com/cpp/CLion-2018.1.2.tar.gz
sudo tar xvf CLion.tar.gz -C /opt

# JetBrains IntelliJ IDEA 2018.1.3
wget -O ./intellij.tar.gz http://download.jetbrains.com/idea/ideaIU-2018.1.3.tar.gz
sudo tar xfz ./intellij.tar.gz -C /opt

# JetBrains Rider 2018.1
wget -O ./rider.tar.gz http://download.jetbrains.com/rider/JetBrains.Rider-2018.1.tar.gz
sudo tar xfz ./rider.tar.gz -C /opt

# JetBrains WebStorm 2018.1.3
wget -O ./webstorm.tar.gz http://download.jetbrains.com/webstorm/WebStorm-2018.1.3.tar.gz
sudo tar xfz ./webstorm.tar.gz -C /opt

# JetBrains Pycharm 2018.1.2
wget -O ./pycharm.tar.gz http://download.jetbrains.com/python/pycharm-professional-2018.1.2.tar.gz
sudo tar xfz ./pycharm.tar.gz -C /opt

# JetBrains DataGrip 2018.1.2
wget -O ./datagrip.tar.gz http://download.jetbrains.com/datagrip/datagrip-2018.1.2.tar.gz
sudo tar xfz ./datagrip.tar.gz -C /opt

# JetBrains GoLand 2018.1.2
wget -O ./goland.tar.gz http://download.jetbrains.com/go/goland-2018.1.2.tar.gz
sudo tar xfz ./goland.tar.gz -C /opt

# Python v3.6.5
wget -O ./python.tar.xz https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tar.xz
tar xf ./python.tar.xz
cd ./Python-3.6.5
./configure
make
make test
sudo make install

# MS SQL Server 2017
sudo curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
sudo add-apt-repository "$(curl https://packages.microsoft.com/config/ubuntu/16.04/mssql-server-2017.list)"
sudo apt-get update
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
sudo add-apt-repository "$(wget -qO- https://packages.microsoft.com/config/ubuntu/16.04/mssql-server-2017.list)"
sudo apt-get update
sudo apt-get install -y mssql-server
sudo /opt/mssql/bin/mssql-conf setup

systemctl status mssql-server 

# MS SQL Server Tools
curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list | sudo tee /etc/apt/sources.list.d/msprod.list
sudo apt-get update 
sudo apt-get install mssql-tools unixodbc-dev

sudo apt-get update 
sudo apt-get install mssql-tools

echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile

echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
source ~/.bashrc

# Docker CE 17.03
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb https://download.docker.com/linux/$(. /etc/os-release; echo "") $(lsb_release -cs) stable"
sudo apt-get update && sudo apt-get install -y docker-ce=$(apt-cache madison docker-ce | sudo grep 17.03 | sudo head -1 | sudo awk '{print }')

# Kibernetes
sudo apt-get update && sudo apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl

sudo docker info | sudo grep -i cgroup
cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
sudo sed -i "s/cgroup-driver=systemd/cgroup-driver=cgroupfs/g" /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

systemctl daemon-reload
systemctl restart kubelet

# PowerShell Core
sudo apt-get install -y powershell


Многоинтерфейсное приложение

Заголовок темы может звучать, для Вас, странно, но довольно часто требуется, чтобы приложение имело сразу несколько вариантов интерфейса. Например, GUI, CLI и/или web. В этой статье пойдёт речь о построении такого приложения с использованием паттерна Observer.
По традиции, я буду вести рассказ на примере. Пример, как всегда, высосан из пальца и мало похож на реальное приложение.
Я уже сказал, что использовать мы будем паттерн Observer, но кроме него, нам понадобится ещё и паттерн MVC (Model-View-Controller). MVC состоит из трёх частей: модель, контроллер и представление. Модель - это те данные, с которыми мы работаем. Контроллер определяет способ работы с данными. Представление - это, не что иное, как интерфейс приложения, с которым взаимодействует пользователь. Если данные достаточно просты, то, часто, модель и контроллер объединяют в одном объекте. В своём примере, я именно так и поступил.
На следующей диаграмме показана упрощённая схема приложения.
Из диаграммы видно, что мы имеем два подприложения: консольное и оконное. MyObject, в данном примере, является как моделью, так и контроллером. Объект класса MyObject содержит список наблюдателей и оповещает их о смене состояние через интерфейс MyObjectObserver.
Предполагается, что мы имеем один объект класса MyObject разделённый между двумя приложениями. Когда одно из приложений меняет состояние объекта, второе приложение реагирует на это и меняет своё отображение.
Думаю, что с теорией всё ясно, давайте посмотрим на код.
package test.core;

import java.util.HashSet;

public class MyObject
{

State state = State.State1;
HashSet<MyObjectObserver> observers = new HashSet<MyObjectObserver>();

public static enum State
{
State1,
State2,
State3
}

public synchronized void addObserver(MyObjectObserver observer)
{
if(observer != null)
observers.add(observer);
}

public synchronized void removeObserver(MyObjectObserver observer)
{
observers.remove(observer);
}

public synchronized void setState(State newState)
{
if(state != newState)
{
State oldState = state;
state = newState;
for(MyObjectObserver observer : observers)
{
observer.onStateChanged(oldState, newState);
}
}
}

public synchronized State getState()
{
return state;
}
}
Так как класс MyObject выполняет роль контроллера в многопоточном приложении, то некоторые методы в нём объявлены  как synchronized.
Далее, интерфейс наблюдателя
package test.core;

public interface MyObjectObserver
{

public void onStateChanged(MyObject.State oldState, MyObject.State newState);
}
Каждое подприложение реализует интерфейс Application
package test;

import test.core.MyObject;

public interface Application
{

public void run(MyObject object);
}
Приложение с консольным интерфейсом могло бы выглядеть следующим образом
package test.cli;

import test.Application;
import test.core.MyObject;
import test.core.MyObjectObserver;
import java.util.Scanner;

public class CliApplication
implements Application, MyObjectObserver
{

private boolean needPrompt = true;

@Override
public void run(MyObject object)
{
Scanner stdin = new Scanner(System.in);
object.addObserver(this);
while(true)
{
showPrompt();
String input = stdin.nextLine().trim();
if(input.equals("q")) break;
int number;
try
{
number = Integer.parseInt(input);
}
catch(NumberFormatException e)
{
System.out.println(String.format("\"%s\" is not number", input));
needPrompt = true;
continue;
}
MyObject.State state;
switch(number)
{
case 1:
state = MyObject.State.State1;
break;
case 2:
state = MyObject.State.State2;
break;
case 3:
state = MyObject.State.State3;
break;
default:
System.out.println(
String.format("\"%d\" is wrong number", number));
needPrompt = true;
continue;
}
object.setState(state);
}
object.removeObserver(this);
}

void showPrompt()
{
if(needPrompt)
{
System.out.print("Enter state number or 'q' for exit:\n" +
" 1: State1\n" +
" 2: State2\n" +
" 3: State3\n: ");
}
needPrompt = false;
}

@Override
public void onStateChanged(MyObject.State oldState, MyObject.State newState)
{
System.out.println(String.format(
"\nObject state was changed from \"%s\" to \"%s\"",
oldState.toString(), newState.toString()));
needPrompt = true;
showPrompt();
}
}
Пользователю предлагается ввести номер состояния, после чего оно меняется в объекте. Кроме того, приложение следит за своим объектом и выводит сообщения о том, что его состояние меняется.
Оконное приложение требует двух классов: приложение, наследник от интерфейса Application и, собственно, окно. Класс GuiApplication является всего лишь средством запуска окна.
package test.gui;

import test.Application;
import test.core.MyObject;


public class GuiApplication
implements Application
{

@Override
public void run(MyObject object)
{
MainWindow window = new MainWindow(object);
window.setDefaultCloseOperation(MainWindow.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Объект класса MyObject транзитом проходит через класс GuiApplication в класс MainWindow.
package test.gui;

import test.core.MyObject;
import test.core.MyObjectObserver;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class MainWindow
extends JFrame
implements MyObjectObserver
{

private JComboBox comboBox;
private MyObject object;

class ComboBoxListener
implements ActionListener
{

@Override
public void actionPerformed(ActionEvent e)
{
MyObject.State state = (MyObject.State)comboBox.getSelectedItem();
if(state != null)
object.setState(state);
}
}

class MainWindowListener
extends WindowAdapter
{

@Override
public void windowOpened(WindowEvent e)
{
object.addObserver(MainWindow.this);
}

@Override
public void windowClosed(WindowEvent e)
{
object.removeObserver(MainWindow.this);
}
}

public MainWindow(MyObject object)
{
this.object = object;
comboBox = new JComboBox(MyObject.State.values());
comboBox.setSelectedItem(object.getState());
comboBox.addActionListener(new ComboBoxListener());
add(comboBox);
addWindowListener(new MainWindowListener());
setMinimumSize(new Dimension(200, 50));
pack();
}

@Override
public void onStateChanged(MyObject.State oldState, MyObject.State newState)
{
comboBox.setSelectedItem(newState);
}
}
Каждое подприложение запускается в отдельном потоке методом main.
package test;

import test.cli.CliApplication;
import test.core.MyObject;
import test.gui.GuiApplication;

class ApplicationRunner
implements Runnable
{

private Application application;
private MyObject object;

public ApplicationRunner(Application application, MyObject object)
{
this.application = application;
this.object = object;
}

@Override
public void run()
{
application.run(object);
}
}

public class Main
{

static MyObject object = new MyObject();

static Application[] getApplications()
{
return new Application[]
{
new CliApplication(),
new GuiApplication()
};
}

public static void main(String[] args)
{
for(Application app : getApplications())
{
ApplicationRunner runner = new ApplicationRunner(app, object);
Thread thread = new Thread(runner);
thread.start();
}
}
}
То, что в итоге получилось, можно увидеть в ролике ниже.

Спецификация исключений: друг или враг?

Исторически сложилось, что разные языки программирования, поддерживающие работу с исключениями, по-разному относятся к спецификации исключений. В Java спецификации обязательны и контролируются статически, в C# и Python их вообще нет, а в C++ этот вопрос является одним из самых "сырых" мест.
В двух словах о том, что такое спецификация исключений, для тех, кто не сталкивался с этим понятием. Спецификация исключений - это явное описание тех типов исключений, которые могут быть сгенерированы некой функцией. В java, языке, который поддерживает наилучшим образом спецификацию исключений, это выглядит так
void funct() throw MyException
{
throw new MyException();
}
MyException - это единственно возможный тип исключений, который может покинуть метод. При спецификации можно указать несколько типов исключений.

C++

С самого начала, идея спецификации исключений была чужда языку C++. Язык унаследовал миллиарды строк кода на языке C и, к тому времени, уже были написаны миллионы строк кода на C++, в которых не было ничего о спецификации исключений. Тем не менее, в C++ была добавлена возможность спецификаций. В своей книге "Дизайн и эволюция C++" Бьёрн Страуструп пишет о том, что спецификации, изначально могли контролироваться только во время исполнения, но, позже, были добавлены некоторые возможности для статического контроля, но, как мы увидим, этого не достаточно. В том же параграфе, Бьёрн приводит пример, который я сейчас Вам продемонстрирую. Итак, предположим, что у нас есть библиотека, написанная на языке C++, вот заголовок этой библиотеки
#ifndef SHAREDCPP_H
#define SHAREDCPP_H

#ifdef __cplusplus
extern "C" {
#endif

void foo();

#ifdef __cplusplus
} // extern "C"
#endif

#endif // SHAREDCPP_H
И её реализация
#include <iostream>
#include <string>

extern "C" {

void foo()
{
std::cout << "Throw exceptionn";
throw std::string("test exception");
}

}
Я назвал эту библиотеку libsharedcpp. Я использую ОС Linux, поэтому я не писал конструкции типа __declspec(dllexport). Если Вы используете ОС MS Windows, то для компиляции примеров, Вам нужно будет добавить эти конструкции (думаю, не нужно Вас учить это делать). Для компиляции, я буду использовать компиляторы из набора GCC. Собираем эту библиотеку командой
g++ -shared -fPIC sharedcpp.cpp -o libsharedcpp.so
Теперь создадим библиотеку libsharedc на языке C со следующим кодом
#ifndef SHAREDC_H
#define SHAREDC_H

#ifdef __cplusplus
extern "C" {
#endif

void bar();

#ifdef __cplusplus
} // extern "C"
#endif

#endif // SHAREDC_H
#include "sharedc.h"
#include "sharedcpp.h"

void bar()
{
foo();
}
Как видно, библиотека libsharedc использует функцию из библиотеки libsharedcpp, поэтому, при компиляции, надо указать этот факт
gcc -shared -fPIC sharedc.c -lsharedcpp -L. -o libsharedc.so
Теперь создадим исполняемый модуль на языке C++.
#include <string>
#include <iostream>
#include "sharedc.h"

int main()
{
try
{
bar();
}
catch(const std::string & str)
{
std::cout << str << std::endl;
}
return 0;
}
Если Вы используете UNIX-like ОС, то, перед компиляцией программы, Вам следует либо поместить собранные библиотеки в место, где Ваша ОС их найдёт, например в /usr/lib, либо добавить в путь для поиска текущий каталог командой
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:`pwd`
Далее собираем программу командой
g++ program.cpp -lsharedc -L. -o program
Запустим программу
$ ./program 
Throw exception
test exception
Итак, что же я хотел продемонстрировать этой программой. Прежде всего, обратите внимание, на то, что исключение прошло через два скомпилированных модуля. Мало того, один из скомпилированных модулей написан на языке C, который знать не знает ни о каких исключениях. Из этого уже можно сделать вывод о том, что указать спецификацию исключений для функции bar невозможно.
Теперь, давайте дополним программу таким образом
#include <string>
#include <exception>
#include <iostream>
#include "sharedc.h"

void test() throw(std::exception)
{
bar();
}

int main()
{
try
{
test();
}
catch(const std::exception & err)
{
}
return 0;
}
Я добавил функцию test, которая специфицирует исключения только стандартного типа - std::exception. Статические анализаторы не смогут определить, что функция bar генерирует исключение типа std::string, и компиляция пройдёт успешно. Но при выполнении нас ждёт ошибка.
$ ./program 
Throw exception
terminate called after throwing an instance of 'std::string'
Аварийный останов
И даже добавление catch блока, обрабатывающего std::string, не устранит проблему , так как она возникает до момента обработки исключения, а именно - в момент выхода исключения из функции.
try
{
test();
}
catch(const std::exception & err)
{
}
catch(const std::string & err)
{
}
Таким образом, я продемонстрировал ситуацию, когда пользы от спецификации исключений меньше, чем вреда.

Java

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

public class MyBase
{

private int value;

public MyBase(int value)
{
this.value = value;
}
}
И от него наследуются множество наследников. Например
public class MyFirst
extends MyBase
{

public MyFirst()
{
super(1);
}
}
И, некоторый наследник содержит в себе данные, исходный код которых, не доступен
package test;

import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.SparseMultigraph;

public class MySecons
extends MyFirst
{

private Graph<Integer, String> graph;

public MySecons()
{
graph = new SparseMultigraph<Integer, String>();
}
}
Внезапно Вам становится необходимо сделать Ваш базовый класс клонируемым и Вы добавляете переопределение метода clone и наследование от интерфейса Cloneable
package test;

public class MyBase
implements Cloneable
{

private int value;

public MyBase(int value)
{
this.value = value;
}

@Override
public Object clone()
{
try
{
MyBase base = (MyBase)super.clone();
base.value = value;
return base;
} catch(CloneNotSupportedException ex)
{
// Этого не должно произойти.
return null;
}
}
}
Я задушил исключение, так как оно не может возникнуть, если класс реализует интерфейс Cloneable. Теперь я должен реализовать метод clone в потомках базового класса. С классом MyFirst проблем нет; он достаточно примитивен, чтобы не писать эту реализацию вообще. Но, вот, класс MySecond принесёт нам не мало хлопот. Склонировать граф из библиотеки JUNG мы не можем, так как он не предоставляет нам такой возможности. После некоторых раздумий, мы можем прийти к выводу, что клонировать граф - действительно не лучшая затея, и мы решаем запретить клонировать объекты класса MySecond. Но мы уже разрешили клонировать базовый класс и всех его детей, поэтому единственным верным решением остаётся выкинуть исключение при попытке вызова метода clone. Но не тут-то было. В java запрещается специфицировать исключения у переопределённых методов таким образом, чтобы это расходилось со спецификацией, определённой в родительском классе (убирать исключения из спецификации можно). И теперь, как ни старайся, ничего с методом clone, дельного не выйдет, либо возвращаем задушенное исключение, либо придумываем другой способ копирования. Я, к слову, смог ввести некое подобие конструкторов копирования из C++. Другой вариант решения проблемы описан в статье "Фабрика клонов".

Итого

Я не могу говорить за большинство языков, которые существуют, но о некоторых сказать кое-что могу. В частности, в языках C# и Python от спецификации исключений отказались вовсе, и правильно сделали, на мой взгляд. Как видно из предыдущих частей статьи: спецификации исключений зачастую оказываются вредными, а в случае с C++, ещё и бесполезными.

Определяем, лежит ли точка внутри полигона

Эта статья является продолжением цикла "Математика в программировании". На этот раз я хочу показать, как можно по координатам определить, лежит ли точка внутри замкнутого многоугольника или нет. Для решения этой задачи существует несколько способов. Оптимальный, для вычислительной машины, способ заключается в подсчёте количества пересечённых сторон лучём проведённым из проверяемой точки.


Как видно из рисунка 1, если точка принадлежит полигону, то луч пересечёт нечётное количество сторон. Если же луч пересечёт чётное количество сторон, или ниодной, то это значит, что точка лежит вне полигона.
У данного способа есть недостаток, связанный с случаем, когда точка проходит через вершину, но в большинстве случаев такой вероятностью можно пренебречь. Кроме того, можно выполнить дополнительные действия для изменения условия, например изменить направление луча, если последний проходит через одну из вершин.
Недавно я начать изучать язык программирования Java и в связи с этим решил реализовать пример к этой статье именно на нём. Это мой первый опус на Java, так что не обессудьте.
Итак, что должно получится в итоге:
В окне можно "натыкать" левой кнопкой мыши точек, которые будут являться вершинами полигона. Во время рисования все линии будут отображаться. Закончить рисовать полигон нужно нажатием правой кнопки, после чего все щелчки левой кнопкой будут приводить к установке точки, вхождение которой нужно проверить. Для того, чтобы начать всё сначала нужно нажать Esc.
Итак, исходники:


Файл CPoints.java - это мой вспомогательный класс, который я использовал, для хранения массивов точек. Он динамически выделяет память под массивы блоками.
package polygon;


public class CPoints
{

// Массив абсцисс.
private int[] m_x;
// Массив ординат.
private int[] m_y;
// Количество точек.
private int m_count;
// Вместимость.
private int m_capacity;
// Количество элементов, добавляемых при расширении.
private final int m_block_size;
// Минимальный размер m_block_size.
private final int m_block_size_minimal = 10;

// Устанавливает размер блока в значение по умолчанию.
public CPoints()
{
m_block_size = m_block_size_minimal;
m_count = 0;
m_capacity = 0;
increase();
}

// default_block_size - размер блока, если меньше чем
// m_block_size_minimal,то игнорируется.
public CPoints(int default_block_size)
{
if(default_block_size < m_block_size_minimal)
default_block_size = m_block_size_minimal;
m_block_size = default_block_size;
m_count = 0;
m_capacity = 0;
increase();
}

// Добавляет точку в конец массива.
public void push(final int x, final int y)
{
// Если добавлять некуда, то увеличиваем размер массивов.
if(m_count == m_capacity)
increase();

m_x[m_count] = x;
m_y[m_count] = y;
m_count++;
}

// Удаляет последнюю точку.
public void pop()
{
if(m_count > 0)
m_count--;
}

/// Возвращает размер массива.
public int count()
{
return m_count;
}

// Возвращает массив X'ов.
public final int[] getXArray()
{
return m_x;
}

// Возвращает массив Y'ов.
public final int[] getYArray()
{
return m_y;
}

// Увеличевает размер массивов на m_block_size.
private void increase()
{
int new_capasity = m_capacity + m_block_size;
if(m_capacity != 0)
{
int[] tempx = new int[new_capasity];
int[] tempy = new int[new_capasity];

for(int i = 0; i < m_capacity; i++)
{
tempx[i] = m_x[i];
tempy[i] = m_y[i];
}

m_x = tempx;
m_y = tempy;
}
else
{
m_x = new int[new_capasity];
m_y = new int[new_capasity];
}
m_capacity = new_capasity;
}
}

Непосредственно сам определитель, модуль CDeterminant.java
package polygon;


public class CDeterminant
{

public static boolean determine(final CPoints points, int x, int y)
{

boolean result = false;
int count = points.count();
int[] xp = points.getXArray();
int[] yp = points.getYArray();


for (int i = 0, j = count - 1; i < count; j = i++)
{
if(xp[j] == xp[i] || yp[j] == yp[i]) // Деление на ноль.
continue;

if(((yp[i] <= y && y < yp[j]) || (yp[j] <= y && y < yp[i])))
{
float x1 = xp[i];
float y1 = yp[i];
float x2 = xp[j];
float y2 = yp[j];

float k = (y2 - y1) / (x2 - x1);
float b = y1 - k * x1;
float cross_x = (y - b) / k;

if((float)x > cross_x)
result = !result;
}
}
return result;
}
}

Здесь я нарочно не стал оптимизировать, даже наоборот, написал всё более подробно для того, чтобы было проще понять. Мы в цикле берём каждую пару смежных точек полигона и находим точку пересечения с лучём f(x) = y, где y - ордината определяемой точки. Луч проводим в левую сторону. Сначала определяем, пересекутся ли вообще отрезки. Затем находим абсциссу пересечения (ордината известна исходя из того, что наш луч параллелен оси абсцисс). Так как все вычисления выполняются в числах с плавающей точкой, то я привёл сразу всё к типу float. Далее восстанавливаем уравнение прямой стороны полигона, находя тангенс угла наклона - коэффициент при x и точку пересечения с осью ординат. Затем вычисляем точку пересечения с лучём. Если точка пересечения оказывается левее точки, то мы получили необходимое пересечение и отмечаем его инвентированием результирующего флага.
Оптимизированную версию этого алгоритма можно найти здесь.

Ну и далее я приведу остальные части программы.
Файл CMainWindow.java с главным окном
package polygon;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

class CPanel
extends JPanel
{

// Флаг того, что полигон был замкнут.
private boolean m_polygon_end;
// Вершины полигона.
private CPoints m_points;
// Абсциса определяемой точки.
private int m_x;
// Ордината определяемой точки.
private int m_y;
// Флаг того, что точка была установлена.
private boolean m_point_end;

public CPanel()
{
m_points = new CPoints();
m_polygon_end = false;
m_point_end = false;

CMouseHandler mouse_handler = new CMouseHandler();
addMouseListener(mouse_handler);
addMouseMotionListener(mouse_handler);
addKeyListener(new CKeyHandler());
setFocusable(true); // Для обработки клавиатурных сообщений.
setBackground(Color.white);
}

@Override public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D graphics = (Graphics2D)g;

if(m_polygon_end)
{
graphics.drawPolygon(m_points.getXArray(),
m_points.getYArray(), m_points.count());
if(m_point_end)
graphics.fillOval(m_x - 5, m_y - 5, 10, 10);
}
else
{
graphics.drawPolyline(m_points.getXArray(),
m_points.getYArray(), m_points.count());
}
}


private class CMouseHandler
extends MouseAdapter
{

@Override public void mouseClicked(MouseEvent event)
{
if(!m_polygon_end)
{
if(event.getButton() == MouseEvent.BUTTON1)
{
m_points.push(event.getX(), event.getY());
repaint();
}
else if(event.getButton() == MouseEvent.BUTTON3)
{
// В полигоне не может быть меньше трёх точек.
if(m_points.count() >= 4)
{
m_points.pop(); // Убираем хвост.
m_polygon_end = true;
repaint();
}
}
}
else if(event.getButton() == MouseEvent.BUTTON1)
{
m_x = event.getX();
m_y = event.getY();
m_point_end = true;
repaint();
String msg = CDeterminant.determine(m_points, m_x, m_y) ?
"Точка лежит внутри полигона" :
"Точка лежит вне полигона";
JOptionPane.showMessageDialog(null, msg, "Положение точки",
JOptionPane.INFORMATION_MESSAGE);
}
}

@Override public void mouseMoved(MouseEvent event)
{
if(!m_polygon_end)
{
m_points.pop();
m_points.push(event.getX(), event.getY());
repaint();
}
}
} // private class CMouseHandler

private class CKeyHandler
extends KeyAdapter
{

@Override public void keyPressed(KeyEvent event)
{
if(event.getKeyCode() == KeyEvent.VK_ESCAPE)
{
m_points = new CPoints();
m_point_end = false;
m_polygon_end = false;
repaint();
}
}
} // private class CKeyHandler
} // class CPanel

public class CMainWindow
extends JFrame
{

public CMainWindow()
{
setTitle("Определение принадлежности точки полигону");
setSize(800, 600);
setBackground(Color.white);

getContentPane().add(new CPanel());
}
}
И файл, запускающий всё это добро - CMain.java:

package polygon;

public class CMain
{

public static void main(String params[])
{
CMainWindow wnd = new CMainWindow();
wnd.setDefaultCloseOperation(CMainWindow.EXIT_ON_CLOSE);
wnd.setVisible(true);
}
}