Здравствуйте, MasterZiv, Вы писали:
MZ>On 09/18/2011 05:18 PM, Galiulin Rishat Faimovich wrote:
>>> > Спасибо за ваше мнение. Я действительно пишу на С++ довольно короткое время — >>> > всего 1 год, до этого я писал на Java. >> >> MZ>Интересно, как же ты эту проблему на Java-то решал. >> >> Никогда не решал такую проблему на Java.
MZ>А что ж на плюсах-то припёрло ? MZ>Одинаковые же проблемы.
Для меня это не явлется проблемой, но большенсво коллег проголосовало за ввод IN, IN_OUT и OUT define-овю.
Как я уже ответил одному из участников, я просто заменил неконтролируемые компилятором define-ы на шаблоны.
Re[9]: Реализация IN, IN-OUT и OUT параметров функций
Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
GRF>Здравствуйте, k.o., Вы писали:
KO>>Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
GRF>>>Вроде написал более рабочий вариант specification.hpp:
KO>>Да, уже лучше, только у нас, по прежнему, не поддерживается преобразование unique_ptr<A> -> unique_ptr<const A>, можно, конечно, сделать его явно:
GRF>Да, действительно выглядит плохо . Но можно немного короче
Так ведь можно и ещё короче, если не использовать эти обёртки для параметров.
KO>>Кроме того, у этого подхода есть ещё, скажем так, концептуальная проблема, из-за того что некоторые способы передачи параметров не укладываются в in, in_out и out. Например, мы хотим передавать владение unique_ptr дальше по цепочке вызовов:
KO>>
Понятно, что саму обёртку можно, без проблем, передать дальше.
KO>>После этого можно будет вспомнить о существовании rvalue-references и необходимости поддерживать perfect forwarding.
GRF>При реализации мы к сожалению не ориетировались на новый стандарт С++
Ну это, как бы, заметно, хотя и странно для, подобного рода, инфраструктурного кода.
Re[9]: Реализация IN, IN-OUT и OUT параметров функций
Соответственно, вместо 'operator+(const MyType& lhs, const MyType& rhs)' у нас должен быть 'operator+(in<MyType&> lhs, in<MyType&> rhs)'. Если это не так, возникает вопрос, в чём разница?
KO>>Или, может, предлагается использовать такой подход не для всех функций? Тогда, пожалуй, стоит перечислить сценарии, в которых предполагается это использовать, в исходном сообщении об этом ничего не было сказано.
GRF>in, in_out и out должны использваться в теле функций или в вызовах функций
Вопрос в том, для каких функций параметры должны объявляться с использованием in, in_out и out?
Re[10]: Реализация IN, IN-OUT и OUT параметров функций
Здравствуйте, k.o., Вы писали:
KO>Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
GRF>>Здравствуйте, k.o., Вы писали:
KO>>>Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
GRF>>>>Вроде написал более рабочий вариант specification.hpp:
KO>>>Да, уже лучше, только у нас, по прежнему, не поддерживается преобразование unique_ptr<A> -> unique_ptr<const A>, можно, конечно, сделать его явно:
GRF>>Да, действительно выглядит плохо . Но можно немного короче
KO>Так ведь можно и ещё короче, если не использовать эти обёртки для параметров.
KO>>>Кроме того, у этого подхода есть ещё, скажем так, концептуальная проблема, из-за того что некоторые способы передачи параметров не укладываются в in, in_out и out. Например, мы хотим передавать владение unique_ptr дальше по цепочке вызовов:
KO>>>
KO>Понятно, что саму обёртку можно, без проблем, передать дальше.
KO>>>После этого можно будет вспомнить о существовании rvalue-references и необходимости поддерживать perfect forwarding.
GRF>>При реализации мы к сожалению не ориетировались на новый стандарт С++
KO>Ну это, как бы, заметно, хотя и странно для, подобного рода, инфраструктурного кода.
Для нас сейчас самое важное надежность и переносимость.
Re[11]: Реализация IN, IN-OUT и OUT параметров функций
А самому? Вас же на ideone.com не забанили, надеюсь? В любом случае, результатом arg() будет константная ссылка на unique_ptr<const A>, у которого, разумеется, нет оператора ().
Попробуйте повторить этот пример используя ваш specification.hpp.
Re[12]: Реализация IN, IN-OUT и OUT параметров функций
KO>А самому? Вас же на ideone.com не забанили, надеюсь? В любом случае, результатом arg() будет константная ссылка на unique_ptr<const A>, у которого, разумеется, нет оператора ().
KO>Попробуйте повторить этот пример используя ваш specification.hpp.
Тогда я не понял ваш переидущий ответ KO>>>Кроме того, у этого подхода есть ещё, скажем так, концептуальная проблема, из-за того что некоторые способы передачи параметров не укладываются в in, in_out и out. Например, мы хотим передавать владение unique_ptr дальше по цепочке вызовов:
KO>>>
Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
GRF>Здравствуйте, k.o., Вы писали:
KO>>Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
GRF>Тогда я не понял ваш переидущий ответ
GRF>Зачем тогда здесь скобки у arg ?
Вы меня спрашиваете для чего предназначен 'operator()' у вашего класаа in?
KO>>Попробуйте повторить этот пример используя ваш specification.hpp.
Re[14]: Реализация IN, IN-OUT и OUT параметров функций
Здравствуйте, k.o., Вы писали:
KO>Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
GRF>>Здравствуйте, k.o., Вы писали:
KO>>>Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
GRF>>Тогда я не понял ваш переидущий ответ
GRF>>Зачем тогда здесь скобки у arg ?
KO>Вы меня спрашиваете для чего предназначен 'operator()' у вашего класаа in?
KO>>>Попробуйте повторить этот пример используя ваш specification.hpp.
Спасибо, понял проблему. Надо об этом подумать.
Re[7]: Реализация IN, IN-OUT и OUT параметров функций
От:
Аноним
Дата:
19.09.11 17:59
Оценка:
Здравствуйте, alexeiz, Вы писали:
А>> Я в первые один-два года на C++ тоже ваял шаблонные кошмары а ля Александреску (я тогда и не знал, кто это такой), потому что прикольно это было. Половину буста переизобрел, не догадываясь о его существовании.
A>Самому себе польстить — святое дело!
Ну если хочешь, могу еще тебе посочувствовать.
Re[8]: Реализация IN, IN-OUT и OUT параметров функций
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, alexeiz, Вы писали:
А>>> Я в первые один-два года на C++ тоже ваял шаблонные кошмары а ля Александреску (я тогда и не знал, кто это такой), потому что прикольно это было. Половину буста переизобрел, не догадываясь о его существовании.
A>>Самому себе польстить — святое дело!
А>Ну если хочешь, могу еще тебе посочувствовать.
Пожалуйста, не превращайте данный топик в "междоусобчик". Нам очень нужны Ваши советы
Re[14]: Реализация IN, IN-OUT и OUT параметров функций
Здравствуйте, k.o., Вы писали:
KO>Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
GRF>>Здравствуйте, k.o., Вы писали:
KO>>>Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
GRF>>Тогда я не понял ваш переидущий ответ
GRF>>Зачем тогда здесь скобки у arg ?
KO>Вы меня спрашиваете для чего предназначен 'operator()' у вашего класаа in?
KO>>>Попробуйте повторить этот пример используя ваш specification.hpp.
Вот измененный вариант specification.hpp
#ifndef SPECIFICATION_HPP
# define SPECIFICATION_HPP
# include <ostream>
# if 1 // in< class Type > - Functions input parameter
/**
* @brief Functions input parameter
* @tparam Type none reference input parameter type
*/template< class Type >
class in
{
template< class InType >
friend in< InType > in_( const InType value );
public:
/**
* Cast operator
* @return input parameter value
*/operator Type&( ) const
{
return value_;
}
/**
* Gets parameter value
* @return input parameter value
*/
Type& operator ( )( ) const
{
return value_;
}
private:
/**
* Initialization constructor
* @param value input parameter value
*/explicit in( Type& value ): value_( value ) { }
template< class OtherType >
in( const OtherType& value ); // disable implicit conversion for none reference objects
/**
* input parameter value
*/
Type& value_;
};
template< class Type >
in< Type& > in_ref_( const Type& value );
/**
* @brief Functions input parameter
* @tparam Type reference input parameter type
*/template< class Type >
class in< Type& >
{
template< class InType >
friend in< InType& > in_ref_( const InType& value );
public:
/**
* Cast operator
* @return input parameter value
*/operator const Type&( ) const
{
return value_;
}
/**
* Cast operator
* @return input parameter value
*/template< class BaseType >
operator in< BaseType& >( ) const
{
return in_ref_< BaseType > ( value_ );
}
/**
* Gets parameter value
* @return input parameter value
*/const Type& operator ( )( ) const
{
return value_;
}
private:
/**
* Initialization constructor
* @param value input parameter value
*/explicit in( const Type& value ): value_( value ) { }
/**
* input parameter value
*/const Type& value_;
};
/**
* @brief Functions input parameter
* @tparam Type pointer input parameter type
*/template< class Type >
class in< Type* >
{
template< class InType > friend in< InType >
in_( const InType value );
public:
/**
* Cast operator
* @return input parameter value
*/operator const Type*( ) const
{
return value_;
}
/**
* Gets parameter value
* @return input parameter value
*/const Type* operator ( )( ) const
{
return value_;
}
/**
* Member selection by pointer operator
* @return input parameter value
*/const Type* operator ->( ) const
{
return value_;
}
private:
/**
* Initialization constructor
* @param value input parameter value
*/explicit in( const Type * const value ): value_( value ) { }
/**
* input parameter value
*/const Type * const value_;
};
/**
* Function call input parameter specifier
* @param value input parameter value
* @return input parameter
*/template< class Type >
in< Type > in_( Type value )
{
return in< Type > ( value );
}
/**
* Function call input parameter by reference specifier
* @param value input parameter value
* @return input parameter
*/template< class Type >
in< Type& > in_ref_( const Type& value )
{
return in< Type& >( value );
}
# endif
# if 1 // in_out< class Type& > - Functions input-output parameter
/**
* @brief Functions input-output parameter
* @tparam Type none pointer input-output parameter type
*/template< class Type >
class in_out
{
template< class InOutType >
friend in_out< InOutType > in_out_( InOutType& value );
public:
/**
* Cast operator
* @return input-output parameter value
*/operator Type&( ) const
{
return value_;
}
/**
* Gets parameter value
* @return input-output parameter value
*/
Type& operator ( )( ) const
{
return value_;
}
/**
* Assignment operator
* @param value assignment value
* @tparam ValueType assignment value type
* @return input-output parameter value
*/template< class ValueType >
Type& operator =(const ValueType& value )
{
return value_ = value;
}
private:
/**
* Initialization constructor
* @param value input-output parameter value
*/explicit in_out( Type& value ): value_( value ) { }
/**
* input-output parameter value
*/
Type& value_;
};
/**
* @brief Functions input-output parameter
* @tparam Type pointer input-output parameter type
*/template< class Type >
class in_out< Type* >
{
template< class InOutType >
friend in_out< InOutType > in_out_( InOutType& value );
public:
/**
* Cast operator
* @return input-output parameter value
*/operator Type* &( ) const
{
return value_;
}
/**
* Gets parameter value
* @return input-output parameter value
*/
Type* & operator ( )( ) const
{
return value_;
}
/**
* Assignment operator
* @param value assignment value
* @tparam ValueType assignment value type
* @return input-output parameter value
*/
Type* & operator =( Type* value )
{
return value_ = value;
}
/**
* Member selection by pointer operator
* @return input-output parameter value
*/
Type* operator ->( ) const
{
return value_;
}
private:
/**
* Initialization constructor
* @param value input-output parameter value
*/explicit in_out( Type* & value ): value_( value ) { }
/**
* input-output parameter value
*/
Type* & value_;
};
/**
* Function call input-output parameter specifier
* @param value input-output parameter value
* @return input-output parameter
*/template< class Type >
in_out< Type >
in_out_( Type& value )
{
return in_out< Type > ( value );
}
# endif
# if 1 // out< class Type* > - Functions output parameter
/**
* @brief Functions output parameter
* @tparam Type none pointer output parameter type
*/template< class Type >
class out
{
template< class OutType >
friend out< OutType > out_( OutType& value );
public:
/**
* Cast operator
* @return output parameter value
*/operator Type&( ) const
{
return value_;
}
/**
* Gets parameter value
* @return output parameter value
*/
Type& operator ( )( ) const
{
return value_;
}
/**
* Assignment operator
* @param value assignment value
* @tparam ValueType assignment value type
* @return output parameter value
*/template< class ValueType >
Type& operator =(const ValueType& value )
{
return value_ = value;
}
/**
* Checks if parameter is used
* @return <code>true</code> if parameter has been set and <code>false</code> otherwise
*/bool
used( )
{
return ( &value_ ) != NULL;
}
/**
* Checks if parameter is not used
* @return <code>true</code> if parameter has not been set and <code>false</code> otherwise
*/bool
not_used( )
{
return !used( );
}
/**
* Creates unused parameter indicator
* @return unused parameter indicator
*/static out
unused( )
{
return out( *reinterpret_cast<Type*> ( NULL ) );
}
private:
/**
* Initialization constructor
* @param value output parameter value
*/explicit out( Type& value ): value_( value ) { }
/**
* output parameter value
*/
Type& value_;
};
/**
* @brief Functions output parameter
* @tparam Type pointer output parameter type
*/template< class Type >
class out< Type* >
{
template< class OutType >
friend out< OutType > out_( OutType& value );
public:
/**
* Cast operator
* @return output parameter value
*/operator Type* &( ) const
{
return value_;
}
/**
* Gets parameter value
* @return output parameter value
*/
Type* & operator ( )( ) const
{
return value_;
}
/**
* Assignment operator
* @param value assignment value
* @tparam ValueType assignment value type
* @return output parameter value
*/
Type* & operator =( Type* value )
{
return value_ = value;
}
/**
* Member selection by pointer operator
* @return output parameter value
*/
Type* operator ->( ) const
{
return value_;
}
/**
* Checks if parameter is used
* @return <code>true</code> if parameter has been set and <code>false</code> otherwise
*/bool
used( )
{
return ( &value_ ) != NULL;
}
/**
* Checks if parameter is not used
* @return <code>true</code> if parameter has not been set and <code>false</code> otherwise
*/bool
not_used( )
{
return !used( );
}
/**
* Creates unused parameter indicator
* @return unused parameter indicator
*/static out
unused( )
{
return out( *reinterpret_cast<Type**> ( NULL ) );
}
private:
/**
* Initialization constructor
* @param value output parameter value
*/explicit out( Type* & value ): value_( value ) { }
/**
* output parameter value
*/
Type* & value_;
};
/**
* Function call output parameter specifier
* @param value output parameter value
* @return output parameter
*/template< class Type >
out< Type >
out_( Type& value )
{
return out< Type > ( value );
}
# endif/**
* Ostream output operator
* @param os output stream
* @param output output value
* @return output stream
* @tparam CharType @a os character type
* @tparam OutputType @a output type
*/template< class CharType, class OutputType >
std::basic_ostream< CharType >& operator <<( in_out< std::basic_ostream< CharType > > os, const OutputType& output )
{
return os( ) << output;
}
#endif/* SPECIFICATION_HPP */
Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
KO>>>>Попробуйте повторить этот пример используя ваш specification.hpp.
GRF>Вроде теперь ваш пример работает
Зато, теперь не работает добавление константности:
void
sink2( in< const std::unique_ptr< const A > > arg )
{
// would like to get compile error for attempts to modify arg, e.g.:
// arg().release();
}
void
sink1( in< std::unique_ptr< const A > > arg )
{
std::cout << "sink1 owns pointer to A: " << arg( ).get( ) << std::endl;
sink2( in_( std::move( arg() ) ) );
std::cout << "sink1 passed ownership to sink2: " << arg( ).get( ) << std::endl;
}
И, хотя, реализовать это нетрудно:
#include <type_traits>
...
template< class Type >
class in
{
public:
in( const in<typename std::remove_const<Type>::type>& other)
: value_(other())
{
}
...
};
Возникает закономерный вопрос: нафига козе баян зачем нам вся эта шаблонная магия с классом in, если мы теперь всё-равно можем менять передаваемые аргументы, а константность нужно добавлять самому, точно также, как и с обычными параметрами? Собственно, в этом и заключается "концептуальная проблема" вашего подхода: он либо слишком ограничивающий, либо, мало чем отличается (кроме лишней писанины) от обычной передачи параметров.
Re[16]: Реализация IN, IN-OUT и OUT параметров функций
Здравствуйте, k.o., Вы писали:
KO>Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
KO>>>>>Попробуйте повторить этот пример используя ваш specification.hpp.
GRF>>Вроде теперь ваш пример работает
KO>Зато, теперь не работает добавление константности: KO>
KO>void
KO>sink2( in< const std::unique_ptr< const A > > arg )
KO>{
KO> // would like to get compile error for attempts to modify arg, e.g.:
KO> // arg().release();
KO>}
KO>void
KO>sink1( in< std::unique_ptr< const A > > arg )
KO>{
KO> std::cout << "sink1 owns pointer to A: " << arg( ).get( ) << std::endl;
KO> sink2( in_( std::move( arg() ) ) );
KO> std::cout << "sink1 passed ownership to sink2: " << arg( ).get( ) << std::endl;
KO>}
KO>
А для каких целей нужно будет добавление константности если полностью перейти на in, in_out, out нотацию?
Как я понимаю in параметр должен гарантировать толко неизменность передаваемого внешнего для функции параметра.
Re[17]: Реализация IN, IN-OUT и OUT параметров функций
Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
GRF>Здравствуйте, k.o., Вы писали:
KO>>Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
KO>>>>>>Попробуйте повторить этот пример используя ваш specification.hpp.
GRF>>>Вроде теперь ваш пример работает
KO>>Зато, теперь не работает добавление константности:
GRF>А для каких целей нужно будет добавление константности если полностью перейти на in, in_out, out нотацию? GRF>Как я понимаю in параметр должен гарантировать толко неизменность передаваемого внешнего для функции параметра.
Многие (я в том числе) предпочитают обеспечивать, также, неизменяемость входных параметров внутри самой функции (см., например, "Совершенный Код" 2-е издание, изд. Питер, с. 173). Вы сами-то здесь
зачем заменили 'int* bar_in' на 'const int* const bar_in'? То, что в некоторых случаях их всё-таки имеет смысл делать изменяемыми, всего лишь, показывает ограниченность модели in/in_out/out (или, если хотите, системы типов C++). В принципе, всё это прекрасно решается введением соглашений об именовании параметров (например, добавлением префиксов in, inOut, out к именам параметров) и code review, но, в этом случае нет никакого смысла и в дополнительных обёртках, таких как ваши.
Re[18]: Реализация IN, IN-OUT и OUT параметров функций
Здравствуйте, k.o., Вы писали:
KO>Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
GRF>>Здравствуйте, k.o., Вы писали:
KO>>>Здравствуйте, Galiulin Rishat Faimovich, Вы писали:
KO>>>>>>>Попробуйте повторить этот пример используя ваш specification.hpp.
GRF>>>>Вроде теперь ваш пример работает
KO>>>Зато, теперь не работает добавление константности:
GRF>>А для каких целей нужно будет добавление константности если полностью перейти на in, in_out, out нотацию? GRF>>Как я понимаю in параметр должен гарантировать толко неизменность передаваемого внешнего для функции параметра.
KO>Многие (я в том числе) предпочитают обеспечивать, также, неизменяемость входных параметров внутри самой функции (см., например, "Совершенный Код" 2-е издание, изд. Питер, с. 173). Вы сами-то здесь
зачем заменили 'int* bar_in' на 'const int* const bar_in'? То, что в некоторых случаях их всё-таки имеет смысл делать изменяемыми, всего лишь, показывает ограниченность модели in/in_out/out (или, если хотите, системы типов C++). В принципе, всё это прекрасно решается введением соглашений об именовании параметров (например, добавлением префиксов in, inOut, out к именам параметров) и code review, но, в этом случае нет никакого смысла и в дополнительных обёртках, таких как ваши.
, убедили и меня и коллег полностью отказаться от введения как IN, IN-OUT и OUT define-ов (они не контролируются компилятором и следовательно могут ввести в заблуждение), так и in, in_out и out шаблонов (они вводят сильные ограничения) и перейти на стандартную модель.
Re[19]: Реализация IN, IN-OUT и OUT параметров функций
, убедили и меня и коллег полностью отказаться от введения как IN, IN-OUT и OUT define-ов (они не контролируются компилятором и следовательно могут ввести в заблуждение), так и in, in_out и out шаблонов (они вводят сильные ограничения) и перейти на стандартную модель.
"Стандартную" это какую? Все-таки, в C++ не решены две проблемы:
1) отличения параметров "in-out" от "out" (разница в том, что в первом случае параметр перед вызовом функции должен быть установлен в определенное значение, от которого зависит выполнение функции, а во втором случае значение параметра до вызова функции неважно);
2) запись аргументов "in-out" и "in" при вызове функции при использовании ссылок (C++ way) ничем не отличается от передачи по значению:
void DoSomething(int inParam1, int inParam2, int& inoutParam, int& outParam)
{
}
int main()
{
int a = 0, b = 0, c = 0, d = 0;
DoSomething(a, b, c, d);
return 0;
}
Я считаю, что пустые дефайны типа INOUT и OUT — все-таки более-менее нормальное решение, исключительно для аннотации.
#define INOUT
#define OUT
void DoSomething(int inParam1, int inParam2, INOUT int& inoutParam, OUT int& outParam)
{
}
int main()
{
int a = 0, b = 0, c = 0, d = 0;
DoSomething(a, b, INOUT c, OUT d);
return 0;
}
Или можно ставить какие-нибудь комментарии.
void DoSomething(int inParam1, int inParam2, /*INOUT*/int& inoutParam, /*OUT*/int& outParam)
{
}
int main()
{
int a = 0, b = 0, c = 0, d = 0;
DoSomething(a, b, /*INOUT*/ c, /*OUT*/ d);
return 0;
}
Честно говоря, мне самому оба решения не нравятся. Для меня это вопрос открытый.
Re[20]: Реализация IN, IN-OUT и OUT параметров функций
On 22.09.2011 0:26, Аноним 492 wrote:
> 1) отличения параметров "in-out" от "out" (разница в том, что в первом случае > параметр перед вызовом функции должен быть установлен в определенное значение, > от которого зависит выполнение функции, а во втором случае значение параметра до > вызова функции неважно);
В отсутствии чистого OUT нет ничего страшного. Этого нет в очень многих
системах программирования.
Если IN в IN_OUT игнорируется телом функции, что ОЧЕНЬ легко и всегда
можно сделать, то проблемы вообще нет.
Во-вторых, можно просто сделать два параметра из одного IN_OUT, один
чисто IN, по значению, другой IN_OUT c семантикой только OUT.
В третьих, можно возвращать значения вообще с помощью того, что возвращает
функция. Не всегда это можно сделать, но можно.
> Я считаю, что пустые дефайны типа INOUT и OUT — все-таки более-менее нормальное > решение, исключительно для аннотации.
Они ни от чего не спасают. Если бы это поддерживал язык, -- да, было бы хорошо.
А так -- всё равно всегда надо смотреть на сигнатуру вызываемой функции и проверять.