Может ли добавление условной диагностики в Java быть нулевой стоимостью, когда они выключены? - PullRequest
1 голос
/ 14 апреля 2019

Это действительно вопрос о Java, а не о c ++.

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

Несколько лет назад я реализовал класс, который я назвал "Отладчик" в C ++, который сделал это. Проект использует перечисления для имен флагов, чтобы вы могли иметь код, который читается, эффективен и безопасен для типов. Использование выглядит следующим образом.

enum DebugBits {
    testCondition1,
    testCondition2,
    testCondition3
    nTestConditions
}`
Debugger testDebug("testDebug", nTestConditions,
                    "condition1",
                    "condition2",
                    "condition3");

Critical::doStuff()
{
    ...
    if (testDebug.on(testCondition2))
        doSomethingSpecial();
    ...
}

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

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

Итак, вот реализация, которая работает и несколько эффективна. Вопросы,

  • Может ли реализация быть более эффективной?
  • В то же время можно ли четко использовать использование в коде?

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

import java.util.BitSet;
import java.util.EnumSet;
import java.util.Vector;

public class Debugger {
    private final EnumSet mEnumSet;
    private final BitSet  mBits;
    private final Vector<String> mNames;

    public Debugger(EnumSet es) {
        mEnumSet = es;
        mBits = new BitSet(es.size());
        mNames = new Vector<>();
        for (Object i : mEnumSet)
            mNames.add(i.toString());
    }

    public void set(int bit) {
        mBits.set(bit);
    }

    public void set(String bitName) {
        int bit = mNames.indexOf(bitName);
        if (bit >= 0)
            mBits.set(bit);
    }

    public boolean on(int bit) {
        return mBits.get(bit);
    }

    public boolean on(Object arg) {
        if (arg.getClass() == Enum.class) {
            int bit = ((Enum)arg).ordinal();
            return mBits.get(bit);
        }
        return false;
    }

    public boolean on(String bitName) {
        int bit = mNames.indexOf(bitName);
        return bit >= 0 && mBits.get(bit);
    }
}

class SampleUsage {
    static class Debug extends Debugger {
        enum Bits {
            zero, one, two, three;
            public static final EnumSet<Bits> bits = EnumSet.allOf(Bits.class);
        }
        public Debug() {
            super(Bits.bits);
        }
    }
    public static final Debug debug = new Debug();

    public SampleUsage() {}

    void doStuff() {
        if (debug.on(Debug.Bits.three))
            showDebugInfo();
        if (debug.on("three"))
            showDebugInfo();
    }

    private void showDebugInfo() {}
}

Ответы [ 2 ]

1 голос
/ 14 апреля 2019

Я думаю, вы упустили пункт EnumSet<>. EnumSet<> - это ваш типобезопасный набор высокоэффективных флагов отладки.

enum Debug {
    FLAG0, FLAG1, FLAG2;
}

EnumSet<Debug> debug = EnumSet.noneOf(Debug.class);

debug.add(Debug.FLAG0);

if (debug.contains(Debug.FLAG0)) {
    showDebugInfo0();   // Will be executed.
}

if (debug.contains(Debug.FLAG1)) {
    showDebugInfo1();   // Will not be executed because FLAG1 was not added to the EnumSet.
}

Нет необходимости переводить значения enum в порядковые и добавлять этот порядковый номер к BitSet. EnumSet<> уже реализован с использованием чего-то вроде BitSet (за исключением того, что EnumSet<> имеет фиксированный размер, основанный на количестве идентификаторов в Enum, поэтому не может быть расширен до произвольной длины).

Если вы хотите проверить, установлен ли флаг по имени, вы можете использовать Enum.valueOf(), чтобы преобразовать имя в правильное Enum, и проверить, содержит ли EnumSet<> это.

if (debug.contains(Enum.valueOf(Debug.class, "FLAG2")) {
    showDebugInfo2();     // Also not executed, because FLAG2 was not added to the EnumSet.
}

Опять же, нет необходимости в Vector<String>, который содержит все Enum имена, которые вы должны найти .indexOf(). Enum поставляется с этим встроенным методом. В любом случае Vector<> не был эффективным выбором, так как Vector<> операции автоматически synchronized, поэтому немного медленнее, чем эквивалент ArrayList<>.

Примечание : незначительная разница: .indexOf() возвращает -1, если не найдено; Enum.valueOf() повысит IllegalArgumentException, если вы дадите ему неизвестное имя идентификатора.


Предполагая, что вы хотите .on(), а не .contains() и хотите, чтобы в вашем коде использовался более простой флаг проверки по имени, нам нужно будет обернуть EnumSet<> в другом классе. Этот класс Debug может выглядеть так:

class Debug<T extends Enum<T>> {
    private final Class<T> enum_class;
    private final EnumSet<T> flags;

    public Debug(Class<T> enum_class) {
        this.enum_class = enum_class;
        flags = EnumSet.noneOf(enum_class);
    }

    public void set(T flag) {
        flags.add(flag);
    }

    public boolean on(T flag) {
        returns flags.contains(flag);
    }

    public void set(String flag_name) {
        flags.add(Enum.valueOf(enum_class, flag_name));
    }

    public boolean on(String flag_name) {
        return flags.contains(Enum.valueOf(enum_class, flag_name));
    }
}
0 голосов
/ 14 апреля 2019

Таким образом, с незначительными изменениями в вашей реализации, он показывает краткость и ясность и использует меньше ресурсов.Enum.valueOf () я пропустил полностью.Преобразование строк - это цель, которую я не описал, но она полезна при попытке установить биты через подсистему, которая не знает о классе, содержащем перечисления, но пользователь знает имена.Я правильно понял это, но ты избавил меня от сорняков.Большое спасибо.

Ох ... и я сменил имя.

import java.util.EnumSet;

class Diagnostic<T extends Enum<T>> {
    private final Class<T> enum_class;
    private final EnumSet<T> flags;

    public Diagnostic(Class<T> enum_class) {
        this.enum_class = enum_class;
        this.flags = EnumSet.noneOf(enum_class);
    }

    public void set(T flag) {
        flags.add(flag);
    }

    public boolean on(T flag) {
        return flags.contains(flag);
    }

    public void set(String flag_name) {
        try {
            flags.add(Enum.valueOf(enumClass, flag_name));
        }
        catch (Exception e) {}
    }

    public boolean on(String flag_name) {
        try {
            return flags.contains(Enum.valueOf(enumClass, flag_name));
        }
        catch (Exception e) {
            return false;
        }
    }
}

class SampleUsage {
    enum DiagBits {
        zero, one, two, three;
    }
    public static final Diagnostic<DiagBits> diag = new Diagnostic<>(DiagBits.class);

    public SampleUsage() {}

    void doStuff() {    
        if (diag.on(DiagBits.three))
            showDebugInfo();
        if (diag.on("three"))
            showDebugInfo();
    }

    private void showDebugInfo() {}
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...