Дружба не передается по наследству - какие есть альтернативы? - PullRequest
8 голосов
/ 05 июля 2011

Я написал / пишу фрагмент кода для анализа физики, первоначально для себя, который, надеюсь, теперь будет использоваться и расширяться небольшой группой физиков.Никто из нас не является гуру C ++.Я собрал небольшую структуру, которая абстрагирует данные о «физическом событии» в объекты, на которые воздействует цепочка инструментов, которые можно легко переключать в зависимости от требований анализа.

Это создало две половины длякод: код «физический анализ», который управляет объектами события и производит наши результаты через производные от базового «инструмента»;и «структурный» код, который присоединяет входные файлы, разбивает задание на параллельные запуски, связывает инструменты в цепочку согласно некоторому сценарию и т. д.

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

Я хотел бы иметь возможность:

  • A) очевидным образом продемонстрировать, что структурный код не редактирует данные события каким-либо образом
  • B) принудительно применить это еще разпользователи начинают расширять код сами (никто из нас не является экспертом, и физика всегда на первом месте - перевод: все, что не получено, является честной игрой для грязного взлома)

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

К сожалению, в этом случае метод вызова получателей / установщиков из базы (который является другом) создаст больше проблем, чем решит -код должен быть максимально чистым, легким для понимания и максимально связанным с физикой при реализации самого инструмента (пользователю не нужно быть экспертом ни в C ++, ни во внутренней работе программы, чтобы создатьинструмент).

Учитывая, что у меня есть надежный базовый класс, и любые производные будут подлежать тщательному анализу, есть ли другой обходной, но хорошо проверенный способ предоставления доступа только к этим производным?Или какой-либо способ запрета доступа к производным какой-то другой базы?


Чтобы прояснить ситуацию, у меня есть что-то вроде

class Event
{
    // The event data (particle collections etc)
};

class Tool
{
    public:
        virtual bool apply(Event* ev) = 0;
};

class ExampleTool : public Tool
{
    public:
        bool apply(Event* ev)
        {
            // do something like loop over the electron collection
            // and throw away those will low energy
        }
};

Идеальным было бы ограничение доступа к содержимомуСобытия только для этих инструментов по двум причинам (A и B) выше.

Спасибо всем за предложенные решения.Я думаю, что, как я и подозревал, идеальное решение, о котором я мечтал, невозможно.Решение dribeas было бы идеальным в любой другой ситуации, но именно в функции apply () код должен быть как можно более четким и лаконичным, поскольку мы будем в основном тратить весь день на написание / редактирование функций apply (), а такженужно понимать каждую строчку из них написанную каждым из остальных.Дело не столько в способностях, сколько в удобочитаемости и усилиях.Мне нравится препроцессорное решение от «Бесполезного».Это на самом деле не навязывает разделение, но кто-то должен быть действительно злым, чтобы сломать его.Для тех, кто предложил библиотеку, я думаю, что это, безусловно, будет хорошим первым шагом, но на самом деле не решает две основные проблемы (так как мне все равно нужно будет предоставить источник).

Ответы [ 3 ]

2 голосов
/ 05 июля 2011

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

2 голосов
/ 05 июля 2011

В C ++ существует три квалификатора доступа: public, protected и private.Предложение с производными физическими инструментами, наследующими доступ от базового класса Tool , по-видимому, указывает на то, что вы хотите protected доступ, но неясно, находятся ли фактические данные, которые private, в Tool(и, следовательно, protected достаточно) или в настоящее время private в классе, который дружит с Tool.

В первом случае просто сделайте данные protected:

class Tool {
protected:
   type data;
};

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

class Data {
   type this_is_private;
   friend class Tool;
};
class Tool {
protected:
   static type& gain_acces_to_data( Data& d ) { 
       return d.this_is_private;
   }
};
class OneTool : public Tool {
public:
   void foo( Data& d ) {
      operate_on( gain_access_to_data(d) );      
   }
};

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

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

struct Evil : Tool {
   static type& break_rule( Data & d ) {
      return gain_access_to_data( d );
   }
};

И теперь каждый может просто использовать Evil как дверь к Data.Я рекомендую вам прочитать C ++ FAQ-lite для более глубокого понимания C ++.

0 голосов
/ 05 июля 2011

Существует также подход в стиле C, ограничивающий видимость, а не права доступа. Он применяется в большей степени в соответствии с соглашением и (в некоторой степени) с вашей системой сборки, а не с языком, хотя вы могли бы использовать своего рода защиту для предотвращения «случайной» утечки деталей реализации Инструмента в структурный код.

-- ToolInterface.hpp --
class Event; // just forward declare it

class ToolStructuralInterface
{
    // only what the structural code needs to invoke tools
    virtual void invoke(std::list<Event*> &) = 0;
};

-- ToolImplementation.hpp --
class Event
{
    // only the tool code sees this header
};
// if you really want to prevent accidental inclusion in the structural code
#define TOOL_PRIVATE_VISIBILITY

-- StructuralImplementation.hpp --
...
#ifdef TOOL_PRIVATE_VISIBILITY
#error "someone leaked tool implementation details into the structural code"
#endif
...

Обратите внимание, что этот вид разбиения позволяет размещать инструментальный и структурный код в отдельных библиотеках - вы даже можете ограничить доступ к структурному коду отдельно для кода инструмента и просто поделиться заголовками и скомпилированной библиотекой.

...