2009-11-23 2 views
1

Прежде чем я попытался сопоставить свои классы и пространства имен, используя статические вызовы, которые я выполнил, и теперь мне нужно отобразить функции моих классов, потому что они будут использоваться динамически.Функции карты класса

Во-первых, я думал о hardcode в конструкторе, поэтому я могу назначить std: map со строкой имени функции, указывающей на саму функцию.

, например:

class A{ 
    int B(){ 
     return 1; 
    } 
}; 

int main(){ 
    A *a = new A(); 
    vector<string, int (*)()> vec; 

    vector["A.B"] = a.B; 
} 

Под этим я сопоставили функцию B на классе, я знаю, что я только отображается функция экземпляра и Thats B не является статическим, чтобы быть глобально сопоставляются.

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

Мой вопрос в том, что я могу сделать это только с помощью hardcoding у конструктора, так как это область экземпляра, о которой мы говорим, или если есть способ сделать это в объявлении функции, как здесь для пространств имен и классы:

Somehow register my classes in a list

+0

Это помогло бы узнать, все ли функции, которые вы выполняете хотите позвонить, имеют одну и ту же подпись. Если да, то как они будут вызваны? Через некоторые * варианты * списки аргументов? –

+0

Глядя на ваш предыдущий вопрос, где упоминаются макросы, мне напоминают библиотеки MFC и ATL. Они делают много таких вещей, когда каждый класс «регистрирует свои собственные методы» (путем включения макросов в объявление класса, которые расширяются до кода, который заполняет какую-то глобальную структуру данных). Это все так же безобразно, как грязь. – Eric

+0

Действительно, то, что вы пытаетесь сделать, действительно неудобно в статически типизированном языке, поскольку указатели функций-членов для разных классов или разных сигнатур функций считаются принципиально разными типами. В C++ 'Foo :: bar (int x)' отличается от 'Foo :: baz (int x, int z)' как 'string' отличается от' float'. Что именно вы пытаетесь достичь? Должен быть лучший подход. –

ответ

3

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

Проблема с использованием обычного указателя функции заключается в том, что нестатическая функция-член не является обычной функцией. Предположим, вы могли бы указать на функцию-член с обычным указателем на функцию, что произойдет, если вы разыменовали этот указатель и вызвали функцию? Функция-член нуждается в объекте для работы, и синтаксис не предоставляет способ передать этот объект.

Вам нужен указатель на элемент, который является слегка скрытой функцией с относительно сложным синтаксисом. В то время как обычный указатель абстрагирует объект, указатель на член реферат участника в классе; указатель указывает, какой член класса должен вызываться, но не тот объект, из которого требуется получить член (который будет указан при использовании указателя). Мы можем использовать это что-то вроде этого:

class B; 

class A 
{ 
    B some_function() 
    { /* ... */ } 
}; 


B (A::* myval)() = A::some_function; 

Здесь myval это переменная, которая указывает один из членов класса А, в этом случае some_function член (хотя это может указывать на какой-либо другой член того же тип). Мы можем передавать myval туда, где захотим (например, хранить его в контейнере STL, как в вашем примере), а затем, когда мы хотим вызвать функцию, мы указываем экземпляр, который должен быть вызван, чтобы найти функцию:

A some_a; 

B newly_created_b = (some_a.*myval)(); 

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

B (Foo::* first_variable)() = Foo::some_function; 
B (Bar::* second_variable)() = Bar::some_function; 

Даже если обе функции могут производить B при вызове без аргументов, два значения работают на различных классов и, следовательно, вы не можете присвоить значение один тип к переменной другого типа. Это, конечно, исключает хранение этих разных типов в одном контейнере STL.

Если вы намерены хранить их в контейнере, вам нужно пойти с помощью основанного на функторе решения, такого как Charles Salvia.

+0

Не то, чтобы я это рекомендовал, но ... ну ... вы * можете * хранить оба типа в коллекции, если вы были готовы сделать небезопасное литье. Или, что еще опаснее, начните играть со «offsetof». – Eric

0

Я должен вызвать правильную функцию экземпляра класса.

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

Если это первый, вам нужен std::map или аналогичный, который позволяет искать экземпляры с их имен.

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

boost.org/doc/libs/1_40_0/libs/serialization/doc/serialization.html

2

Если я вас правильно понимаю, вы будете иметь класс, как:

struct Foo 
{ 
    int bar(); 
}; 

и пользователь введите строку типа «Foo :: bar», и из этой строки вам нужно вызвать функцию-член Foo :: bar?

Если это так, довольно сложно использовать гибкое решение на C++ из-за системы статического типа. Вы можете использовать std::map, где ключ является строкой, а значение является указателем функции-члена (или std::mem_fun_t), но это будет работать только на одном классе и только на функциях-членах с одинаковой сигнатурой.

Вы могли бы сделать что-то вроде:

#include <iostream> 
#include <map> 
#include <functional> 

struct Foo 
{ 
int bar() { std::cout << "Called Foo::bar!" << std::endl; } 
}; 

int main() 
{ 
std::map<std::string, std::mem_fun_t<int, Foo> > m; 
m.insert(std::make_pair("Foo::bar", std::mem_fun(&Foo::bar))); 

Foo f; 
std::map<std::string, std::mem_fun_t<int, Foo> >::iterator it = m.find("Foo::bar"); 
std::mem_fun_t<int, Foo> mf = it->second; 
mf(&f); // calls Foo::bar 
} 
0

Вы делаете это в какой-то узкой петле, где вам нужна эффективная карта? Если это так, то указатели функций-членов (как вы уже говорили выше) - это хороший способ. (По крайней мере, после того, как вы столкнулись с проблемой @Tim, упомянутой о том, как держать указатели функций-членов в разных типах в одной коллекции ... давайте начнем с языка!)

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

if(funcName=="A.b") 
{ 
    A a; 
    a.b(); 
} else 
// etc etc etc 

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

switch(parse(funcName)) 
{ 
    case A_b: 
    { 
     A a; 
     a.b(); 
    } 
    break; 
} 

(Конечно, это ломается, если вы хотите, чтобы заполнить список возможностей из разных мест ... например, если каждый класс будет регистрироваться во время запуска, но если у вас есть такая инфраструктура объектов, вы должны сначала использовать интерфейсы вместо указателей!)

+0

Этот подход подвержен ошибкам и не масштабируется - и вы не можете делать все это через интерфейсы, если вам нужно, например, выставлять функциональные возможности для некоторых языков сценариев. –

+0

@gf: Каждое решение проблемы (по запросу) подвержено ошибкам и плохо масштабируется. По крайней мере, этот подход безопасен по типу и использует простой синтаксис. – Eric

+0

@ Эрик: Извините, если это звучит слишком суровым, но есть более эффективные решения, а затем явные переключения случаев, которые также безопасны для типов. С явным переключением вы выполняете, imo, репликацию и избыточную работу (там, где вы можете делать ошибки), когда центральная однострочная регистрация в сочетании со стандартной функцией диспетчеризации/вызова будет достаточной :) –