#ifdef #ifndef на Java


106

Я сумніваюся, чи є спосіб зробити умови компіляції в Java на зразок #ifdef #ifndef в C ++.

Моя проблема полягає в тому, що алгоритм написаний на Java, і у мене різний час роботи покращується до цього алгоритму. Тому я хочу оцінити, скільки часу економлять, коли використовується кожне поліпшення.

Зараз у мене є набір булевих змінних, які використовуються для вирішення під час роботи, яке поліпшення слід використовувати, а яке - ні. Але навіть тестування цих змінних впливає на загальний час роботи.

Тому я хочу знайти спосіб вирішити під час компіляції, які частини програми слід компілювати та використовувати.

Хтось знає спосіб зробити це на Java. А може хтось знає, що такого способу немає (це теж було б корисно).

Відповіді:


126
private static final boolean enableFast = false;

// ...
if (enableFast) {
  // This is removed at compile time
}

Умови, як показано вище, оцінюються під час компіляції. Якщо замість цього ви використовуєте це

private static final boolean enableFast = "true".equals(System.getProperty("fast"));

Тоді будь-які умови, залежні від enableFast, будуть оцінені компілятором JIT. Накладні витрати на це незначні.


Це рішення краще, ніж моє. Коли я намагався ініціалізувати змінні із заданим зовнішнім значенням, час роботи повернувся до 3 секунд. Але коли я визначив змінні як змінні статичного класу (а не локальну змінну функції), час роботи повернувся до 1 секунди. Дякую за допомогу.
jutky

6
IIRC, це працювало навіть до того, як у Java був компілятор JIT. javacЯ думаю, що код було видалено . Це спрацювало лише в тому випадку, якщо вираз для (скажімо) enableFastявляло собою постійний вираз часу компіляції.
Стівен C

2
Так, але це умовне повинно знаходитися в межах методу, правильно? Як щодо випадку, коли ми маємо купу приватних статичних кінцевих рядків, які ми хотіли б встановити. (наприклад, набір URL-адрес сервера, які встановлюються по-різному для виробництва та постановки)
tomwhipple

3
@tomwhipple: вірно, плюс це не дозволяє робити щось на кшталт: private void foo(#ifdef DEBUG DebugClass obj #else ReleaseClass obj #endif )
Zonko

3
як щодо імпорту (наприклад, щодо classpath)?
n611x007

44

javac не виведе компільований код, який недоступний. Використовуйте кінцеву змінну, встановлену на постійне значення для вашого, #defineі звичайний ifоператор для #ifdef.

Ви можете використовувати javap, щоб довести, що недоступний код не включений у вихідний файл класу. Наприклад, врахуйте наступний код:

public class Test
{
   private static final boolean debug = false;

   public static void main(String[] args)
   {
       if (debug) 
       {
           System.out.println("debug was enabled");
       }
       else
       {
           System.out.println("debug was not enabled");
       }
   }
}

javap -c Test дає наступний висновок, вказуючи на те, що лише один з двох контурів був складений у (а оператор if не був):

public static void main(java.lang.String[]);
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #3; //String debug was not enabled
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

2
Це специфічний javac, чи ця поведінка фактично гарантована JLS?
Pacerier

@pacerier, я не маю уявлення, чи це гарантовано JLS, але це правда для кожного компілятора Java, на який я стикався з 90-х років, за можливим винятком до 1.1.7, і тільки тому, що я цього не зробив тестуйте його потім.

12

Я думаю, що я знайшов рішення, це набагато простіше.
Якщо я визначаю булеві змінні з "остаточним" модифікатором, компілятор Java сам вирішує проблему. Тому що заздалегідь відомо, що було б результатом перевірки цього стану. Наприклад цей код:

    boolean flag1 = true;
    boolean flag2 = false;
    int j=0;
    for(int i=0;i<1000000000;i++){
        if(flag1)
            if(flag2)
                j++;
            else
                j++;
        else
            if(flag2)
                j++;
            else
                j++;
    }

працює на моєму комп’ютері близько 3 секунд.
І цей

    final boolean flag1 = true;
    final boolean flag2 = false;
    int j=0;
    for(int i=0;i<1000000000;i++){
        if(flag1)
            if(flag2)
                j++;
            else
                j++;
        else
            if(flag2)
                j++;
            else
                j++;
    }

працює близько 1 секунди. У той самий час, коли цей код займає

    int j=0;
    for(int i=0;i<1000000000;i++){
        j++;
    }

1
Це цікаво. Здається, JIT вже підтримує умовну компіляцію! Чи працює, якщо ті фінали в іншому класі чи іншому пакеті?
joeytwiddle

Чудово! Тоді я вважаю, що це повинна бути оптимізація виконання, код фактично не знімається під час компіляції. Це добре, якщо ви використовуєте зрілий VM.
joeytwiddle

@joeytwiddle, Ключове слово "до тих пір, поки ви використовуєте" зрілий VM.
Pacerier

2

Ніколи не використовував його, але це існує

JCPP - це повна, сумісна, автономна, чиста Java-реалізація препроцесора C. Він призначений для використання людям, що пишуть компілятори у стилі С на Java, використовуючи такі інструменти, як sablecc, antlr, JLex, CUP тощо. Цей проект був використаний для успішної обробки більшої частини вихідного коду бібліотеки GNU C. З версії 1.2.5 він також може попередньо обробити бібліотеку Apple Objective C.

http://www.anarres.org/projects/jcpp/


1
Я не впевнений, що це відповідає моїй потребі. Мій код написаний на Java. Можливо, ви пропонуєте мені отримати їх джерела та використовувати їх для попередньої обробки мого коду?
jutky

2

Якщо вам справді потрібна умовна компіляція, і ви використовуєте Ant , ви, можливо, зможете відфільтрувати свій код і здійснити пошук і заміну в ньому.

Наприклад: http://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html

Таким же чином ви можете, наприклад, написати фільтр для заміни LOG.debug(...);з /*LOG.debug(...);*/. Це все-таки виконується швидше, ніжif (LOG.isDebugEnabled()) { ... } речі, не кажучи вже про більш стислий водночас.

Якщо ви використовуєте Maven , є подібна функція описана тут .


2

Колектор забезпечує повністю інтегрований препроцесор Java (ніяких кроків збірки або створеного джерела). Він орієнтований виключно на умовну компіляцію та використовує директиви у стилі С.

Препроцесор Java Manifold


1

Використовувати заводський зразок для перемикання між реалізаціями класу?

Час створення об’єкта зараз не може не хвилювати? Якщо усереднювати протягом тривалого періоду часу, найбільший компонент витраченого часу повинен бути в основному алгоритмі, чи не так?

Строго кажучи, вам не потрібен препроцесор, щоб робити те, чого ви прагнете досягти. Ймовірно, є інші способи задоволення вашої вимоги, ніж той, який я запропонував, звичайно.


Зміни дуже незначні. Як і тестування деяких умов, щоб заздалегідь знати потрібний результат замість того, щоб перерахувати його. Тож накладні виклики до функції можуть бути для мене невідповідними.
вигук

0
final static int appFlags = context.getApplicationInfo().flags;
final static boolean isDebug = (appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.