Передача объекта, возвращаемого функцией, в другую функцию.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 26.09.19 16:17
Оценка:
Привет всем.

Есть интерфейс t_errors.

У него две реализации — t_errors_without_limits и t_errors_with_limits

Есть два метода, возвращающие экземпляры этих реализацией:
t_errors_without_limits get_errors_without_limits();

t_errors_with_limits get_errors_with_limits();

Теперь есть утилита, работающая с t_errors:
void add_current_errors(t_errors& errs); //errs внутри не запоминается.

Она вызывается приблизительно так:
add_current_errors(ctx.get_errors_without_limits());
//...
add_current_errors(ctx.get_errors_with_limits());

Все компилируется (VC++) и работает как и ожидается.

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

Когда включаешь режим 'совместимости/соответствия' (Conformance Mode=Yes), компилятор отказывается компилировать.

Переделал так:
void add_current_errors(t_errors*);
//...
add_current_errors(&ctx.get_errors_without_limits());
//...
add_current_errors(&ctx.get_errors_with_limits());

Все откомпилировалось и работает без проблем. Предупреждений 4-го уровня тоже нет.

Где-то в глубине души меня беспокоит такая конструкция func1(&func2()). Но она компилируется и работает. То есть, временный объект, возвращаемый из func2, живет до конца вызова func1.

Как сделать лучше — не знаю.

Собственно вопрос — это нормальный подход или могут возникнуть проблем?

Еще раз отмечу, что add_current_errors указатель/ссылку на t_errors внутри не запоминает.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Передача объекта, возвращаемого функцией, в другую функцию.
От: night beast СССР  
Дата: 26.09.19 16:26
Оценка: +3
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>У него две реализации — t_errors_without_limits и t_errors_with_limits


КД>Есть два метода, возвращающие экземпляры этих реализацией:

КД>
КД>t_errors_without_limits get_errors_without_limits();

КД>t_errors_with_limits get_errors_with_limits();
КД>

КД>Теперь есть утилита, работающая с t_errors:
КД>
КД>void add_current_errors(t_errors& errs); //errs внутри не запоминается.
КД>

КД>Она вызывается приблизительно так:
КД>
КД>add_current_errors(ctx.get_errors_without_limits());
КД>//...
КД>add_current_errors(ctx.get_errors_with_limits());
КД>

КД>Все компилируется (VC++) и работает как и ожидается.

КД>При включенном 4-ом уровне предупреждений, компилятор предупреждает, что нельзя передавать по ссылке результат метода.


errs внутри add_current_errors изменяется? если нет, поставь const.
какой другой компилятор тебя и за взятие адреса rvalue отругает, так что не решение.
Re: Передача объекта, возвращаемого функцией, в другую функц
От: B0FEE664  
Дата: 26.09.19 16:30
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Теперь есть утилита, работающая с t_errors:

КД>
КД>void add_current_errors(t_errors& errs); //errs внутри не запоминается.
КД>


Тут должен быть const :
void add_current_errors(const t_errors& errs);


КД>Собственно вопрос — это нормальный подход или могут возникнуть проблем?

Подход не нормальный в том смысле, что не соответствует современным нормам.
Проблемы возникнуть могут, но они связаны не с работоспособностью кода (код рабочий), а с вопросами возникающими у читателя этого кода:
— если вы меняете errs внутри add_current_errors, то зачем вы это делаете?
— если вы не меняете errs внутри add_current_errors, то зачем он не const?

PS моя телепатия мне подсказывает, что у интерфейса t_errors есть виртуальные get-методы не обявленные const.
И каждый день — без права на ошибку...
Отредактировано 26.09.2019 16:37 B0FEE664 . Предыдущая версия .
Re: Передача объекта, возвращаемого функцией, в другую функц
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 26.09.19 16:54
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

Надо было немного более подробно описать задачу:

КД>Есть интерфейс t_errors.


С виртуальным методом void add(t_error* pError)=0;

КД>У него две реализации — t_errors_without_limits и t_errors_with_limits


Соответственно, эти два класса реализуют метод add.

КД>Теперь есть утилита, работающая с t_errors:

void add_current_errors(t_errors& errs); //errs внутри не запоминается.

Эта утилита вызывает errs.add(...)

UPD1. Название add_current_errors не очень удачное. Это утилита, которая добавляет новые объекты в errs.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Отредактировано 26.09.2019 17:02 DDDX . Предыдущая версия .
Re[2]: Передача объекта, возвращаемого функцией, в другую функц
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 26.09.19 16:57
Оценка:
Здравствуйте, B0FEE664, Вы писали:

КД>>Теперь есть утилита, работающая с t_errors:

void add_current_errors(t_errors& errs); //errs внутри не запоминается.

BFE>Тут должен быть const :
void add_current_errors(const t_errors& errs);

Нет, const тут не нужен, потому что в errs добавляются новые объекты (описания ошибок).

t_errors — это интерфейс формируемой коллекции ошибок.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Передача объекта, возвращаемого функцией, в другую функцию.
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 26.09.19 17:06
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Как сделать лучше — не знаю.


КД>Собственно вопрос — это нормальный подход или могут возникнуть проблем?


КД>Еще раз отмечу, что add_current_errors указатель/ссылку на t_errors внутри не запоминает.


Кстати, проблему можно было задавить так:

add_current_errors(*&ctx.get_errors_without_limits());
//...
add_current_errors(*&ctx.get_errors_with_limits());


Но я решил что это уже совсем ... плохо
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Передача объекта, возвращаемого функцией, в другую функц
От: andrey.desman  
Дата: 26.09.19 17:19
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Нет, const тут не нужен, потому что в errs добавляются новые объекты (описания ошибок).


errs меняется, но потом тут же уничтожается. В чем смысл? Почему get_errors*() возвращает объект, а не ссылку на объект долгоживущий?
Re[4]: Передача объекта, возвращаемого функцией, в другую функц
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 26.09.19 18:03
Оценка:
Здравствуйте, andrey.desman, Вы писали:

КД>>Нет, const тут не нужен, потому что в errs добавляются новые объекты (описания ошибок).


AD>errs меняется, но потом тут же уничтожается. В чем смысл? Почему get_errors*() возвращает объект, а не ссылку на объект долгоживущий?


Да, errs меняется, потом уничтожается. Но в случае

add_current_errors(/*errs*/ctx.get_errors_without_limits());


все будет сохранено в ctx.

Потому что объекты, возвращаемые get_errors_without_limits и get_errors_with_limits, внутри себя хранят только указатель на ctx, которому они все передают.

То есть get_errors_without_limits возвращает объект, который просто передает все, что в него засовывают, в свой родительский объект (ctx).

А get_errors_with_limits возвращает объект, который сначала проверяет ограничения, а потом передает добавляемые данные в свой родительский объект (ctx).

-----------
Я тут посмотрел на свою писанину... Сдается мне, начальное решение (с передачей по ссылке) было нормальным.

А то что оно не собирается в Conformance Mode=Yes, это проблемы этого Mode. Меня никто насильно его включать не заставляет
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[5]: Передача объекта, возвращаемого функцией, в другую фу
От: night beast СССР  
Дата: 26.09.19 18:21
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>А то что оно не собирается в Conformance Mode=Yes, это проблемы этого Mode. Меня никто насильно его включать не заставляет


то что оно собирается в принципе -- это следствие того что когда-то майкрософт забил на стандарт.
другие компиляторы это не соберут.
если очень хочется, то или сделай add_current_errors шаблонным add_current_errors(TErr&&),
или в базу добавь метод, и вручную вызывай его:
struct t_errors
{
    t_errors& get_ref() { return *this; }
};
Отредактировано 26.09.2019 18:22 night beast . Предыдущая версия .
Re: Передача объекта, возвращаемого функцией, в другую функц
От: rg45 СССР  
Дата: 26.09.19 18:46
Оценка: +3
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Есть интерфейс t_errors.

КД>У него две реализации — t_errors_without_limits и t_errors_with_limits
КД>Есть два метода, возвращающие экземпляры этих реализацией:
КД>Теперь есть утилита, работающая с t_errors:

КД>
КД>void add_current_errors(t_errors& errs); //errs внутри не запоминается.
КД>


КД>Все компилируется (VC++) и работает как и ожидается.

КД>При включенном 4-ом уровне предупреждений, компилятор предупреждает, что нельзя передавать по ссылке результат метода.
КД>Когда включаешь режим 'совместимости/соответствия' (Conformance Mode=Yes), компилятор отказывается компилировать.

Да добавь просто перегрузку для rvalue ссылки и всех делов:

void add_current_errors(t_errors& errs); //errs внутри не запоминается.

void add_current_errors(t_errors&& errs) { add_current_errors(errs); }
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 26.09.2019 18:51 rg45 . Предыдущая версия . Еще …
Отредактировано 26.09.2019 18:50 rg45 . Предыдущая версия .
Re: Передача объекта, возвращаемого функцией, в другую функц
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 27.09.19 04:36
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Привет всем.


Тут одна мысль посетила, благодаря rg45
Автор: rg45
Дата: 26.09.19
. Но я решил с ней повременить.
  И привести нормальный пример описываемой проблемы (VS2019)
////////////////////////////////////////////////////////////////////////////////
#include <iostream>

////////////////////////////////////////////////////////////////////////////////
//class t_errors

class t_errors
{
 public:
  virtual void add(int errCode)=0;
};

////////////////////////////////////////////////////////////////////////////////
//class t_context

class t_context
{
 private:
  t_context(const t_context&)=delete;
  t_context& operator = (const t_context&)=delete;

 public:
  class tag_errs_without_limits:public t_errors
  {
   public:
    explicit tag_errs_without_limits(t_context* const pCtx):m_pCtx(pCtx){;}

    virtual void add(int errCode)override {m_pCtx->helper__add_error(errCode);}

   private:
    t_context* m_pCtx;
  };//class tag_errs_without_limits

 public:
  class tag_errs_with_limits:public t_errors
  {
   public:
    explicit tag_errs_with_limits(t_context* const pCtx):m_pCtx(pCtx){;}

    virtual void add(int errCode)override {/*check limits*/; m_pCtx->helper__add_error(errCode);}

   private:
    t_context* m_pCtx;
  };//class tag_errs_with_limits

 public:
  t_context()
  {}

  tag_errs_without_limits get_errors_without_limits()
  {
   return tag_errs_without_limits(this);
  }//get_errors_without_limits

  tag_errs_with_limits get_errors_with_limits()
  {
   return tag_errs_with_limits(this);
  }//get_errors_with_limits

 private:
  void helper__add_error(int const errCode)
  {
   std::cout<<"helper__add_error: "<<errCode<<"!\n";
  }
};

////////////////////////////////////////////////////////////////////////////////

//add_error - может быть виртуальным методом некоторого класса

void add_error(t_errors& errs,int const errCode)
{
 errs.add(errCode);
}//add_error

////////////////////////////////////////////////////////////////////////////////

int main()
{
 t_context ctx;

 add_error(ctx.get_errors_without_limits(),1);
 add_error(ctx.get_errors_with_limits(),2);

 return 0;
}//main
////////////////////////////////////////////////////////////////////////////////

  Как это сейчас модно говорить, Real World Code (RWC) выглядит так:
//...
  virtual void GetData_AddErrorRecord__CantGetData
                               (typename call_ctx_type::errors_type& Errors,
                                const TBaseColumnsInfoImpl*          pColumnsInfo,
                                const DBBINDING&                     Binding,
                                const void*                          pData,
                                HRESULT                              get_data_hr)=0;
//...
template<class TStorageTraits>
HRESULT TMemRowsetStorageROImpl<TStorageTraits>::GetData
                                    (call_ctx_type&                    CallCtx,
                                     TRowBmk                     const Bmk,
                                     const TBindingArray*        const pBindingArray,
                                     const TBaseColumnsInfoImpl* const pColumnsInfo,
                                     void*                       const pData)
{
 //....
     if(get_data_hr!=S_OK)
     {
      this->GetData_AddErrorRecord__CantGetData
       (CallCtx.ErrorsWithLimit(), //<---------------- Вот проблемная точка
        pColumnsInfo,
        *pBinding,
        pData,
        get_data_hr); //no throw

      ++cntErrors;
     }//if
 //....
}//GetData
//...

Когда Conformance Mode=Yes, пример не компилируется:
Error    C2664     'void add_error(t_errors &,const int)': cannot convert argument 1 from 't_context::tag_errs_without_limits' to 't_errors &'
Error    C2664     'void add_error(t_errors &,const int)': cannot convert argument 1 from 't_context::tag_errs_with_limits' to 't_errors &'

Как я тут уже отмечал
Автор: Коваленко Дмитрий
Дата: 26.09.19
, ошибку компиляции можно задавить с помощью *&. Но вылезут предупреждения 4-го уровня:
 add_error(*&ctx.get_errors_without_limits(),1); //warning C4238:  nonstandard extension used: class rvalue used as lvalue
 add_error(*&ctx.get_errors_with_limits(),2); //warning C4238:  nonstandard extension used: class rvalue used as lvalue

Можно переделать add_error, чтобы он получал указатель на errs. Тоже компилируется с предупреждениями 4-го уровня:
void add_error(t_errors* errs,int const errCode);
//...
 add_error(&ctx.get_errors_without_limits(),1); //warning C4238:  nonstandard extension used: class rvalue used as lvalue
 add_error(&ctx.get_errors_with_limits(),2); //warning C4238:  nonstandard extension used: class rvalue used as lvalue
//...


Как по мне, начальный вариант со ссылкой выглядел лучше.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Отредактировано 27.09.2019 5:01 DDDX . Предыдущая версия .
Re[2]: Передача объекта, возвращаемого функцией, в другую функц
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 27.09.19 05:14
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Здравствуйте, Коваленко Дмитрий, Вы писали:


КД>>Привет всем.


КД>Тут одна мысль посетила, благодаря rg45
Автор: rg45
Дата: 26.09.19
. Но я решил с ней повременить.

КД>И привести нормальный пример описываемой проблемы (VS2019)

  t_context можно переделать вот так:
class t_context
{
 private:
  t_context(const t_context&)=delete;
  t_context& operator = (const t_context&)=delete;

 public:
  class tag_errs_without_limits:public t_errors
  {
   public:
    explicit tag_errs_without_limits(t_context* const pCtx):m_pCtx(pCtx){;}

    virtual void add(int errCode)override {m_pCtx->helper__add_error(errCode);}

   private:
    t_context* m_pCtx;
  };//class tag_errs_without_limits

 public:
  class tag_errs_with_limits:public t_errors
  {
   public:
    explicit tag_errs_with_limits(t_context* const pCtx):m_pCtx(pCtx){;}

    virtual void add(int errCode)override {/*check limits*/; m_pCtx->helper__add_error(errCode);}

   private:
    t_context* m_pCtx;
  };//class tag_errs_with_limits

 public:
  t_context()
   :m_errs_without_limits(this)
   ,m_errs_with_limits(this)
  {}

  tag_errs_without_limits& get_errors_without_limits()
  {
   return m_errs_without_limits;
  }//get_errors_without_limits

  tag_errs_with_limits& get_errors_with_limits()
  {
   return m_errs_with_limits;
  }//get_errors_with_limits

 private:
  void helper__add_error(int const errCode)
  {
   std::cout<<"helper__add_error: "<<errCode<<"!\n";
  }

 private:
  tag_errs_without_limits m_errs_without_limits;
  tag_errs_with_limits m_errs_with_limits;
};//class t_context

Тогда пример собирается с Conformance Mode=yes и без предупреждений 4-го уровня.

Как запасной вариант, если компилятор однажды загонит в угол
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Передача объекта, возвращаемого функцией, в другую функц
От: Igore Россия  
Дата: 27.09.19 06:52
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Здравствуйте, B0FEE664, Вы писали:


КД>>>Теперь есть утилита, работающая с t_errors:

КД>
КД>void add_current_errors(t_errors& errs); //errs внутри не запоминается.
КД>

BFE>>Тут должен быть const :
КД>
КД>void add_current_errors(const t_errors& errs);
КД>

КД>Нет, const тут не нужен, потому что в errs добавляются новые объекты (описания ошибок).

КД>t_errors — это интерфейс формируемой коллекции ошибок.

Передавай тогда по значению, и меняй внутри как хочешь.
void add_current_errors(t_errors errs);
Re[4]: Передача объекта, возвращаемого функцией, в другую функц
От: rg45 СССР  
Дата: 27.09.19 07:23
Оценка: +3
Здравствуйте, Igore, Вы писали:

КД>>
КД>>void add_current_errors(const t_errors& errs);
КД>>

КД>>Нет, const тут не нужен, потому что в errs добавляются новые объекты (описания ошибок).
КД>>t_errors — это интерфейс формируемой коллекции ошибок.

I>Передавай тогда по значению, и меняй внутри как хочешь.

I>void add_current_errors(t_errors errs);

Ну и будет срезка типа. А если t_errors — чисто абстрактный класс, то и вовсе ошибка компиляции.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Передача объекта, возвращаемого функцией, в другую функц
От: B0FEE664  
Дата: 27.09.19 07:53
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

Я переделал бы так:
class t_context
{
 private:
  t_context(const t_context&)=delete;
  t_context& operator = (const t_context&)=delete;

 public:
  class tag_errs_without_limits:public t_errors
  {
   public:
    explicit tag_errs_without_limits(t_context* const pCtx):m_pCtx(pCtx){;}

    virtual void add(int errCode)override {m_pCtx->helper__add_error(errCode);}

   private:
    t_context* m_pCtx;
  };//class tag_errs_without_limits

 public:
  class tag_errs_with_limits:public t_errors
  {
   public:
    explicit tag_errs_with_limits(t_context* const pCtx):m_pCtx(pCtx){;}

    virtual void add(int errCode)override {/*check limits*/; m_pCtx->helper__add_error(errCode);}

   private:
    t_context* m_pCtx;
  };//class tag_errs_with_limits

private:
  tag_errs_without_limits m_TagErrsWithoutLimits;
  tag_errs_with_limits    m_TagErrsWithLimits;
  public:
  t_context()
    : m_TagErrsWithoutLimits(this),
      m_TagErrsWithLimits(this)
  {}

  tag_errs_without_limits& get_errors_without_limits()
  {
   return m_TagErrsWithoutLimits;
  }//get_errors_without_limits

  tag_errs_with_limits& get_errors_with_limits()
  {
   return m_TagErrsWithLimits;
  }//get_errors_with_limits

 private:
  void helper__add_error(int const errCode)
  {
   std::cout<<"helper__add_error: "<<errCode<<"!\n";
  }
};
И каждый день — без права на ошибку...
Re[3]: Передача объекта, возвращаемого функцией, в другую функц
От: rg45 СССР  
Дата: 27.09.19 08:04
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

BFE>Я переделал бы так:

BFE>
BFE>  t_context()
BFE>    : m_TagErrsWithoutLimits(this),
BFE>      m_TagErrsWithLimits(this)
BFE>  {}
BFE>


Использование this в списке инициализации может создавать варнинги.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Передача объекта, возвращаемого функцией, в другую функц
От: Mr.Delphist  
Дата: 27.09.19 16:05
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:


КД>Тут одна мысль посетила, благодаря rg45
Автор: rg45
Дата: 26.09.19
. Но я решил с ней повременить.

КД>[cut=И привести нормальный пример описываемой проблемы (VS2019)]

Не знаю, у меня на VS2017 этот пример сразу выдаёт на add_error-вызовах:

error C2664: 'void add_error(t_errors &,const int)': cannot convert argument 1 from 't_context::tag_errs_without_limits' to 't_errors &'
error C2664: 'void add_error(t_errors &,const int)': cannot convert argument 1 from 't_context::tag_errs_with_limits' to 't_errors &'


Настройки дефолтные, даже warning level не менял. Точно этот код?
Re[3]: Передача объекта, возвращаемого функцией, в другую функц
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 27.09.19 17:52
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>Не знаю, у меня на VS2017 этот пример сразу выдаёт на add_error-вызовах:

MD>

MD>error C2664: 'void add_error(t_errors &,const int)': cannot convert argument 1 from 't_context::tag_errs_without_limits' to 't_errors &'
MD>error C2664: 'void add_error(t_errors &,const int)': cannot convert argument 1 from 't_context::tag_errs_with_limits' to 't_errors &'


MD>Настройки дефолтные, даже warning level не менял. Точно этот код?


  Поставь здесь NO
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Передача объекта, возвращаемого функцией, в другую функцию.
От: TailWind  
Дата: 29.09.19 13:15
Оценка:
t_errors E;  GetErrors(E); // получаем ошибки

DoSomething(E);  // обрабатываем
Re: Передача объекта, возвращаемого функцией, в другую функцию.
От: Went  
Дата: 01.10.19 11:04
Оценка:
Здравствуйте, Коваленко Дмитрий.
Если я правильно понял, то сами реализации "знают" о ctx, и их методы сами эти реализации не меняют, а меняют в самом ctx.
Почему бы не передать по константной ссылке эти реализации и сами методы не сделать константными?
Можно привести аналогию с указателями. Указатель на константу и константный указатель на не-константу — разные вещи. В вашем случае реализация — это условный константный указатель на не константный
объект ctx, поэтому ее методы по отношению к самой себе — константны.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.