Форум программистов, компьютерный форум CyberForum.ru

Выбор кода при компиляции - C++

Восстановить пароль Регистрация
 
Variag
1 / 1 / 0
Регистрация: 21.10.2011
Сообщений: 24
Записей в блоге: 1
24.06.2014, 13:47     Выбор кода при компиляции #1
Здравствуйте!
Столкнулся с почти аналогичной проблемой, как и описанная в данной статье:
http://www.solarix.ru/for_developers...e-switch.shtml
В ней идет разговор о выборе кода при компиляции в зависимости от типа входных данных. Моя же проблема, по сравнению со статьей, усложняется (а может и упрощается) тем, что нужно в зависмости от типа данных передавать в функцию еще и разное число параметров, а не одинаковое их число одинаковых типов как в статье.
То есть нужно написать функцию (функции), чтобы алгоритм был примерно такой:
C++
1
2
3
4
5
6
7
template < typename T > void SomeFunction(T t, int x)
{
if (/*тип T удовлетворяет условию*/)
OtherFunction(t);
else
OtherFunction(t, x);
}
Буду признателен, если мне помогут.
P.S. Только заранее прошу не предлагать мне использовать альтернативные пути решения задачи, а предлагать только решения методами шаблонного программирования )))
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Vourhey
Почетный модератор
6469 / 2244 / 123
Регистрация: 29.07.2006
Сообщений: 12,635
24.06.2014, 13:54     Выбор кода при компиляции #2
Если в зависимости от типа нужно выполнять определенный код, то просто специализируй шаблон функции. Безо всяких if внутри.
Variag
1 / 1 / 0
Регистрация: 21.10.2011
Сообщений: 24
Записей в блоге: 1
24.06.2014, 13:56  [ТС]     Выбор кода при компиляции #3
Я бы так и сделал, если бы, типов было только 2, но их несколько побольше и для каждого специализацию писать лениво)))
Но в конце концов я так и сделаю, если иного решения не найдется))
Vourhey
Почетный модератор
6469 / 2244 / 123
Регистрация: 29.07.2006
Сообщений: 12,635
24.06.2014, 13:59     Выбор кода при компиляции #4
Цитата Сообщение от Variag Посмотреть сообщение
и для каждого специализацию писать лениво)))
А в чем разница-то? Ты тут так же будешь для каждого типа будешь писать свой if.
Variag
1 / 1 / 0
Регистрация: 21.10.2011
Сообщений: 24
Записей в блоге: 1
24.06.2014, 14:04  [ТС]     Выбор кода при компиляции #5
а так я их разделю по общему признаку на основе <type_traits> типа std::is_integral или std::is_floating_point. Признак писать короче чем специализацию для каждого типа)
Vourhey
Почетный модератор
6469 / 2244 / 123
Регистрация: 29.07.2006
Сообщений: 12,635
24.06.2014, 14:18     Выбор кода при компиляции #6
Цитата Сообщение от Variag Посмотреть сообщение
а так я их разделю по общему признаку на основе <type_traits> типа std::is_integral или std::is_floating_point
Это уже уточнение. Тогда да, короче.
Variag
1 / 1 / 0
Регистрация: 21.10.2011
Сообщений: 24
Записей в блоге: 1
25.06.2014, 11:28  [ТС]     Выбор кода при компиляции #7
Ну а все-таки) Неужели никто не знаком в достаточной мере с программированием шаблонов?))
Vourhey
Почетный модератор
6469 / 2244 / 123
Регистрация: 29.07.2006
Сообщений: 12,635
25.06.2014, 11:30     Выбор кода при компиляции #8
Variag, ты вопрос задай по-нормальному. Не понятно, что тебе нужно. Написал ты свой пример того, как собираешься делать. И в чем вопрос-то? Чем тебя твой "алгоритм" конкретно не устраивает?
ForEveR
Модератор
Эксперт C++
 Аватар для ForEveR
7927 / 4709 / 318
Регистрация: 24.06.2010
Сообщений: 10,524
Завершенные тесты: 3
25.06.2014, 15:34     Выбор кода при компиляции #9
Выглядить оно будет как-то так (но можно и через специализации конечно). А дальше нужны уточнения от вас.

C++
1
2
3
4
5
6
7
template < typename T > void SomeFunction(T t, int x)
{
if (!std::is_integral<T>::value)
   OtherFunction(t);
else
   OtherFunction(t, x);
}
Variag
1 / 1 / 0
Регистрация: 21.10.2011
Сообщений: 24
Записей в блоге: 1
25.06.2014, 17:02  [ТС]     Выбор кода при компиляции #10
Да, наверное, я не совсем точно и не совсем полно указал исходные данные...
Уточню: предложенный Вами метод не будет работать. Связано это с тем, что компилятору же надо оттранслировать обе ветки if-else, а типы T, для которых вся эта бодяга и мутится, фактически делятся на два типа. Допустим, для простоты, что делается это по указанному Вами признаку std::is_integral<T>, хотя это, конечно, не так. Но проблема в том, что для одних типов функция OtherFunction() принимает один аргумент, а для других - два. Текст этих функций будет примерно такой:
C++
1
2
3
4
5
6
7
8
9
template < typename T > void OtherFunction(T t)
{
t.SomeMethod();
}
 
template < typename T > void OtherFunction(T t, int x)
{
t.SomeMethod(x);
}
В принципе, эти две функции OtherFunction(), можно, наверное, вообще опустить, как лишнее промежуточное звено...
Соответственно, здесь будет ошибка компиляции по причинам не соответствия количества аргументов для методов, потому как для одних типов SomeMethod() принимает int в качестве значения, а для других нет. Может так понятнее будет?
Вы меня извините, это мой первый опыт шаблонного программирования, и я пока плоховато мыслю в категориях шаблонов))
0x10
2425 / 1597 / 232
Регистрация: 24.11.2012
Сообщений: 3,919
25.06.2014, 18:33     Выбор кода при компиляции #11
Читаю и все равно ничего не могу понять. Примеры неправдоподобные и надуманные. Могу от фонаря привести такой кусок кода, но более чем уверен, что у Вас случай гораздо проще и, вероятно, есть ошибки в подходе к решению задачи.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
 
template <class T>
typename std::enable_if<!std::is_integral<T>::value, void>::type
Function(T obj)
{
    std::cout << "Some non-integral: " << obj << std::endl;
}
 
template <class T>
typename std::enable_if<std::is_integral<T>::value, void>::type
Function(T obj)
{
    std::cout << "Some integral: " << obj << std::endl;
}
 
int main()
{
    Function(0x10);
    Function(0.10);
}
Variag
1 / 1 / 0
Регистрация: 21.10.2011
Сообщений: 24
Записей в блоге: 1
25.06.2014, 22:13  [ТС]     Выбор кода при компиляции #12
Могу от фонаря привести такой кусок кода
Вот именно что от фонаря. Такой пример и я видел на просторах интеренета, но он мне не подходит. Вообще не в тему))

Добавлено через 40 минут
Уж коли пошла такая пьянка, сформулирую еще раз проблему:
Есть у меня классы: ClassA, ClassB, ClassC, ClassD, ... ClassZ. В каждом из этих классов есть метод. Только в одних классах он параметров не принимает и выглядит как void SomeMethod(), а в других принимает int и выглядит как void SomeMethod(int x).
Задача:
Написать глобальную шаблонную функцию template < typename T > void SomeFunction(T t, int x), которая будет в зависимости от типа T вызывать соответствующий этому типу метод SomeMethod(), либо с параметром, либо без.
Надеюсь, стало понятнее)) Еще раз извиняюсь, если раньше было менее понятно.
И прошу, все-таки не обсуждать мой подход к решению задачи. Я пробовал и все классы унаследовать от базового, и наследовать от двух базовых, и много чего еще, но все это мне не подходит по разным причинам. Так что давайте все-таки сосредоточимся на предлагаемой мной задаче, а не будем переписывать условие)))
gray_fox
What a waste!
 Аватар для gray_fox
1244 / 1127 / 53
Регистрация: 21.04.2012
Сообщений: 2,350
Завершенные тесты: 3
26.06.2014, 00:25     Выбор кода при компиляции #13
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Variag,
Так?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <utility>
#include <type_traits>
 
 
namespace detail {
 
template<typename T>
class has_noarg_foo_method {
    
    using yes = char(&)[1];
    using no  = char(&)[2];
    
    template<typename>
    struct helper;
    
    template<typename>
    static no  check(...);
    
    template<typename U>
    static yes check(helper<decltype(std::declval<U>().foo())> *);
 
public:
   static constexpr bool value = sizeof (check<T>(nullptr)) == sizeof (yes);
};
 
}
 
template<typename T>
struct has_noarg_foo_method : std::integral_constant<bool, detail::has_noarg_foo_method<T>::value> {};
 
 
struct one {
    
   void foo() const {
      std::cout << "foo()" << std::endl;
   }
};
 
struct two {
    
   void foo(int x) const {
      std::cout << "foo(" << x << ")" << std::endl;
   }
};
 
namespace detail {
    
enum class enabler;
 
}
 
template<typename T>
using enable_if = typename std::enable_if<T::value, detail::enabler>::type;
 
template<typename T>
using disable_if = typename std::enable_if<!T::value, detail::enabler>::type;
 
template<typename T, enable_if<has_noarg_foo_method<T>>...>
void do_foo(T const& obj, int) {
   obj.foo();
}
 
template<typename T, disable_if<has_noarg_foo_method<T>>...>
void do_foo(T const& obj, int const x) {
   obj.foo(x);
}
 
 
int main() {
   do_foo(one{}, 1);
   do_foo(two{}, 2);
}
http://ideone.com/VqHAFR
DrOffset
6458 / 3832 / 885
Регистрация: 30.01.2014
Сообщений: 6,628
26.06.2014, 04:13     Выбор кода при компиляции #14
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Цитата Сообщение от Variag Посмотреть сообщение
Вот именно что от фонаря. Такой пример и я видел на просторах интеренета, но он мне не подходит. Вообще не в тему))
Еще как в тему.
Такие вещи через enable_if как раз и делаются. Только предикат нужно другой.
Код для С++03 (enable_if в этом случае можно взять из boost, либо написать самому, как я) - реализация через SFINAE:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <cstdio>
 
struct ClassA
{
    void SomeMethod()      { puts(__PRETTY_FUNCTION__); }
};
 
struct ClassB
{
    void SomeMethod(int a) { puts(__PRETTY_FUNCTION__); }
};
 
struct ClassC
{
    void SomeMethod()      { puts(__PRETTY_FUNCTION__); }
};
 
template <typename T>
struct has_param
{
    struct param    {};
    struct no_param { char a[2]; };
 
    static param    test(void (T::*)(int));
    static no_param test(void (T::*)());
 
    enum
    {
        value = sizeof(test(&T::SomeMethod)) == 1
    };
};
 
template <bool, class T = void>
struct enable_if
{};
 
template <class T>
struct enable_if<true, T>
{
  typedef T type;
};
 
template <typename T>
typename enable_if<has_param<T>::value, void>::type
    SomeFunction(T t, int x)
{
    t.SomeMethod(x);
}
 
template <typename T>
typename enable_if<!has_param<T>::value, void>::type
    SomeFunction(T t, int x)
{
    t.SomeMethod();
}
 
int main()
{
    ClassA a;
    ClassB b;
    ClassC c;
 
    SomeFunction(a, 1);
    SomeFunction(b, 1);
    SomeFunction(c, 1);
}
Код для С++11 будет немного отличаться, но в целом принцип тот же. Могу привести, если надо, но щас лень.

А вообще я бы сделал так (без шаблонных изысков, старая добрая перегрузка):
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <cstdio>
 
struct ClassA
{
    void SomeMethod()      { puts(__PRETTY_FUNCTION__); }
};
 
struct ClassB
{
    void SomeMethod(int a) { puts(__PRETTY_FUNCTION__); }
};
 
struct ClassC
{
    void SomeMethod()      { puts(__PRETTY_FUNCTION__); }
};
 
template <typename T>
inline void some_function_proxy(T & t, void (T::*ptr)(), int)
{
    (t.*ptr)();
}
template <typename T>
inline void some_function_proxy(T & t, void (T::*ptr)(int), int x)
{
    (t.*ptr)(x);
}
 
template <typename T>
void SomeFunction(T t, int x)
{
    some_function_proxy(t, &T::SomeMethod, x);
}
 
int main()
{
    ClassA a;
    ClassB b;
    ClassC c;
 
    SomeFunction(a, 1);
    SomeFunction(b, 1);
    SomeFunction(c, 1);
}
Добавлено через 3 часа 36 минут
Кстати в С++11, есть довольно красивый и простой способ обобщить проверку сигнатуры функции. Ее можно использовать при решении задачи ТС:
иллюстрация
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <type_traits>
#include <iostream>
 
struct ClassA
{
    void SomeMethod()      {}
};
 
struct ClassB
{
    void SomeMethod(int a) { }
};
 
struct ClassC
{
    void SomeMethod()      { }
};
 
void SomeFunc(int a) { }
 
//-----
 
template <typename T>
struct remove_class_from_member;
 
template <typename Ret, typename Class, typename ...Args>
struct remove_class_from_member<Ret (Class::*)(Args...)>
{
    typedef Ret (*type)(Args...);
};
 
template <typename Ret, typename ...Args>
struct remove_class_from_member<Ret (*)(Args...)>
{
    typedef Ret (*type)(Args...);
};
 
template <typename T, typename U>
struct check_sig
    : std::is_same<typename remove_class_from_member<T>::type, typename std::decay<U>::type>
{};
 
int main()
{
    std::cout << check_sig<decltype(&ClassA::SomeMethod), void()>::value << '\n';
    std::cout << check_sig<decltype(&ClassB::SomeMethod), void(int)>::value << '\n';
    std::cout << check_sig<decltype(&ClassC::SomeMethod), void()>::value << '\n';
    std::cout << check_sig<decltype(&SomeFunc), void(int)>::value << '\n';
}
Variag
1 / 1 / 0
Регистрация: 21.10.2011
Сообщений: 24
Записей в блоге: 1
26.06.2014, 08:58  [ТС]     Выбор кода при компиляции #15
Еще как в тему.
Такие вещи через enable_if как раз и делаются. Только предикат нужно другой.
Спасибо, знаю что через enable_if, просто то решение не соответствовало поставленной задачи.
Спасибо gray_fox и DrOffset за Ваши решения. Теперь хотелось бы пару комментариев для поднятия образованности.
Как я понимаю, здесь ведь enable_if используется для "включения/выключения" возвращаемого значения функции, и таким образом, "включает/выключает" всю функцию?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
template <typename T>
typename enable_if<has_param<T>::value, void>::type
    SomeFunction(T t, int x)
{
    t.SomeMethod(x);
}
 
template <typename T>
typename enable_if<!has_param<T>::value, void>::type
    SomeFunction(T t, int x)
{
    t.SomeMethod();
}
За метод проверки сигнатуры функции отдельное спасибо)
0x10
2425 / 1597 / 232
Регистрация: 24.11.2012
Сообщений: 3,919
26.06.2014, 10:28     Выбор кода при компиляции #16
Цитата Сообщение от Variag Посмотреть сообщение
Как я понимаю, здесь ведь enable_if используется для "включения/выключения" возвращаемого значения функции, и таким образом, "включает/выключает" всю функцию?
Смотрим на возможный вариант реализации: http://en.cppreference.com/w/cpp/types/enable_if
В специализации для false отсутствует вложенный тип type, следовательно все определение
C++
1
2
3
template <typename T>
typename enable_if<false, void>::type
SomeFunction(T t, int x)
становится невалидным, но это приводит не к ошибке компиляции, а к исключению функции из списка кандидатов (см идиому SFINAE).
ForEveR
Модератор
Эксперт C++
 Аватар для ForEveR
7927 / 4709 / 318
Регистрация: 24.06.2010
Сообщений: 10,524
Завершенные тесты: 3
26.06.2014, 11:01     Выбор кода при компиляции #17
В С++11 можно так же развлечься и без создания структур для поиска. Ну это так, на всякий случай, использовать такое не призываю.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
template<typename T, typename E>
constexpr auto has_function_with_no_arg(T&& obj, const E& e = E()) -> decltype((obj.*e)(), void());
 
template<typename T, typename E>
constexpr auto has_function_with_one_arg(T&& obj, const E& e = E()) -> decltype((obj.*e)(int()), void());
 
template<typename T>
auto SomeFunction(T&& obj, int) -> decltype(has_function_with_no_arg(std::forward<T>(obj), &T::func))
{
   obj.func();
}
 
template<typename T>
auto SomeFunction(T&& obj, int x) -> decltype(has_function_with_one_arg(std::forward<T>(obj), &T::func))
{
   obj.func(x);
}
 
struct A
{
   void func()
   {
      std::cout << "func" << std::endl;
   }
};
 
struct B
{
   void func(int x)
   {
      std::cout << "func(int)" << std::endl;
   }
};
 
int main()
{
   SomeFunction(A(), 1);
   SomeFunction(B(), 1);
}
Добавлено через 12 минут
Ну и конечно можно без усложнений.

C++
1
2
3
4
5
6
7
8
9
10
11
template<typename T>
auto SomeFunction(T&& obj, int) -> decltype(obj.func(), void())
{
   obj.func();
}
 
template<typename T>
auto SomeFunction(T&& obj, int x) -> decltype(obj.func(x), void())
{
   obj.func(x);
}
Однако в отличии от enable_if я не могу здесь придумать как сделать вариант данной функции с понятным сообщением ошибки для всех остальных случаев.
Variag
1 / 1 / 0
Регистрация: 21.10.2011
Сообщений: 24
Записей в блоге: 1
26.06.2014, 11:27  [ТС]     Выбор кода при компиляции #18
Проверил, все работает. Еще раз спасибо)
Если уж совсем обнахалиться, то вот еще какой вопрос...
Есть ли возможность написать функцию SomeFunction() таким образом, чтобы она в зависимости от типа принимала то одно то два значения, дабы не было в программе варнингов из-за лишних аргументов функции?
Чтобы вызов функции выглядел как:
SomeFunction(t, x);
Но для определенных типов t, второй параметр игнорировался бы и варнигов бы не было
Хотя... из области фантастики, врят-ли такое возможно)) Но помечтать то можно))
ForEveR
Модератор
Эксперт C++
 Аватар для ForEveR
7927 / 4709 / 318
Регистрация: 24.06.2010
Сообщений: 10,524
Завершенные тесты: 3
26.06.2014, 11:35     Выбор кода при компиляции #19
Variag, Так он ведь итак игнорируется в тех примерах которые вам давали. Дабы перестали быть ворнинги просто уберите имя переменной в функции, которая ее не использует.
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
26.06.2014, 11:41     Выбор кода при компиляции
Еще ссылки по теме:

C++ Каким будет результат компиляции и выполнения данного кода?
C++ При компиляции кода вылазит ошибка "error C2027: use of undefined type 'SldWorks'"
Ошибка при компиляции простого кода с указателем C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Variag
1 / 1 / 0
Регистрация: 21.10.2011
Сообщений: 24
Записей в блоге: 1
26.06.2014, 11:41  [ТС]     Выбор кода при компиляции #20
Точно) Просто привык имя переменной всегда писать))
Yandex
Объявления
26.06.2014, 11:41     Выбор кода при компиляции
Ответ Создать тему
Опции темы

Текущее время: 14:02. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru