Как избавиться от лишних аргументов
От: PlusMyTwitterFace  
Дата: 25.09.12 03:44
Оценка:
Можно ли каким-то образом "избавиться" от "лишних" аргументов функции?

Например, есть у меня, допустим, функция со следующим объявлением:

void foo(std::size_t* _1, float* _2, double* _3, int* _4);


При её вызове в некоторых местах мне не нужно передавать ей _1, _2 и _3 для заполнения, лишь _4. Однако из-за того, что в качестве параметров используются указатели, мне всё равно приходится создавать дополнительные переменные:

std::size_t _1;
float _2;
double _3;
int _4; // Единственная важная для нас переменная в данном случае

foo(&_1, &_2, &_3, &_4);


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

В C99 и C11 эту проблему можно решить при помощи compound literals:

foo((std::size_t[]){0}, (float[]){0.0f}, (double[]){0.0}, &_4);


В C++ на ум приходит лишь два решения:

— Создать отдельную функцию foo_wrapper, которая будет создавать "лишние аргументы" на своей стороне, а в качестве аргумента принимать лишь действительно необходимый. Не самый лучший вариант, поскольку в разные моменты времени "необходимыми" бывают совершенно разные аргументы. Более того, таких функций достаточно много, и писать для каждой из них такие врапперы не самая лучшая идея.

— Создать в file_scope переменные всех фундаментальных типов с приставкой "dummy", которые, собственно, и будут использоваться для передачи в такие функции. Также не самый лучший вариант — лишнее засорение глобального пространства имён. Кроме того, в подобные функции иногда передаются даже User-defined types, а писать dummy-версии для каждого типа, используемого в программе — это какой-то ужас.

Передаваться нули тоже не вариант — никто не гарантирует, что внутри этих функций будут сделаны соответствующие проверки.

Что посоветуете сделать?
Re: Как избавиться от лишних аргументов
От: Centaur Россия  
Дата: 25.09.12 05:00
Оценка:
Здравствуйте, PlusMyTwitterFace, Вы писали:

PMT>Можно ли каким-то образом "избавиться" от "лишних" аргументов функции?


PMT>Например, есть у меня, допустим, функция со следующим объявлением:


PMT>void foo(std::size_t* _1, float* _2, double* _3, int* _4);

PMT>При её вызове в некоторых местах мне не нужно передавать ей _1, _2 и _3 для заполнения, лишь _4. Однако из-за того, что в качестве параметров используются указатели, мне всё равно приходится создавать дополнительные переменные:

Как-то так:
template<typename T>
class _
{
public:
    operator T*() const { return v_; }
private:
    T v_;
};
…
int four;
foo(_<size_t>(), _<float>(), _<double>(), &four);

Интересно было бы добиться синтаксиса foo(_, _, _, &four) для любого типа (с использованием шаблонного operator T*?) и при этом сохранить автоматическую деструкцию ненужного объекта.
Re: Как избавиться от лишних аргументов
От: AleksandrN Россия  
Дата: 25.09.12 05:47
Оценка: 2 (2) +2
Здравствуйте, PlusMyTwitterFace, Вы писали:

PMT>Например, есть у меня, допустим, функция со следующим объявлением:


PMT>
PMT>void foo(std::size_t* _1, float* _2, double* _3, int* _4);
PMT>


PMT>При её вызове в некоторых местах мне не нужно передавать ей _1, _2 и _3 для заполнения, лишь _4. Однако из-за того, что в качестве параметров используются указатели, мне всё равно приходится создавать дополнительные переменные:


PMT>
PMT>std::size_t _1;
PMT>float _2;
PMT>double _3;
PMT>int _4; // Единственная важная для нас переменная в данном случае

PMT>foo(&_1, &_2, &_3, &_4);
PMT>



Передавай NULL, а внутри функции проверяй, если переменная != NULL, то работай с ней, а иначе — ничего не делай.
Re[2]: Один из вариантов -- объект-механизм
От: Erop Россия  
Дата: 25.09.12 05:50
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Здравствуйте, PlusMyTwitterFace, Вы писали:


PMT>>Можно ли каким-то образом "избавиться" от "лишних" аргументов функции?


Может подойдёт
Автор: Erop
Дата: 12.01.07
...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Как избавиться от лишних аргументов
От: Константин Россия  
Дата: 25.09.12 06:12
Оценка:
Здравствуйте, PlusMyTwitterFace, Вы писали:

PMT>Можно ли каким-то образом "избавиться" от "лишних" аргументов функции?

PMT>Например, есть у меня, допустим, функция со следующим объявлением:

void foo(std::size_t* _1, float* _2, double* _3, int* _4);


PMT>Передаваться нули тоже не вариант — никто не гарантирует, что внутри этих функций будут сделаны соответствующие проверки.



Если функция принимает указатель, то она, вроде бы, обязана проверять на ноль. Для остального есть возможность передавать по ссылке.
Разве только это библиотечный код, который нельзя менять.
Re: Как избавиться от лишних аргументов
От: Pavel Dvorkin Россия  
Дата: 25.09.12 10:04
Оценка:
Здравствуйте, PlusMyTwitterFace, Вы писали:

PMT>Можно ли каким-то образом "избавиться" от "лишних" аргументов функции?


PMT>Например, есть у меня, допустим, функция со следующим объявлением:


PMT>
PMT>void foo(std::size_t* _1, float* _2, double* _3, int* _4);
PMT>


PMT>Что посоветуете сделать?



struct PARAM
{
std::size_t _1;
float* _2;
double* _3;
int* _4;
};

void foo(PARAM* param);

// вызов

PARAM param;
memset(&param, 0, sizeof(PARAM);
// установи нужные поля. Остальные будут NULL.
foo(&param);
With best regards
Pavel Dvorkin
Re: Как избавиться от лишних аргументов
От: maykie Россия  
Дата: 25.09.12 10:53
Оценка:
Здравствуйте, PlusMyTwitterFace, Вы писали:

nullptr уже предлагали.

PMT>на своей стороне, а в качестве аргумента принимать лишь действительно необходимый. Не самый лучший вариант, поскольку в разные моменты времени "необходимыми" бывают совершенно разные аргументы.


Ну сделай несколько врапперов. Делов-то.

PMT> Более того, таких функций достаточно много

и?

> Создать в file_scope переменные всех фундаментальных типов с приставкой "dummy", которые, собственно, и будут использоваться для передачи в такие функции


я б не стал этого делать ни в коем случае.

Во-первых, многопоточность. Ну объявишь ты `std::vector<int> dummy_vector_int`, а вдруг два потока захотят её использовать. А вдруг два потока одновременно её изменят. Тут и сложно отлавливаемый сегфолт в лицо получить можно.

Во-вторых, это расстроит оптимизатор. Например:

int dummy_a;//дамми
int z1(int* z){*z = 1;} //ф-ция не все аргументы мы хотим использовать.

int c(){ //мы здесь
  z1(dummy_a);//dummy_a=1, используем глобальную
  int x;
  z1(x);//x=1, используем локальную
}


z1 заинлайнится. Теперь такое дело — x=1 можно выкиунть, ибо dead code и x никем не считывается. Мы не можем сказать то же самое про dummy_a=1, так как у нас тупо нет сведений, что она никем не используется.

То есть фактически при использовании глобальных объектов получится менее оптимальный код.
Re: Как избавиться от лишних аргументов
От: Сыроежка  
Дата: 25.09.12 11:09
Оценка:
Очевидно, что вам просто нужно перегрузить функцию с нужным количеством аргументов.
Меня можно встретить на www.cpp.forum24.ru
Re[2]: Как избавиться от лишних аргументов
От: watch-maker  
Дата: 25.09.12 12:18
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Интересно было бы добиться синтаксиса foo(_, _, _, &four) для любого типа


Можно, наверно, сделать как-то так:
template <typename T>
class Optional {
protected:
    T Data;
    bool Initialized;
public:
    Optional(): Initialized(false) {}
    Optional(const T& d) : Data(d), Initialized(true) {}
    operator T() {return Data; }
    bool IsSet() const {return Initialized; }
    const T& Get() const { return Data; }
};

struct TH {
    template <class T>
    operator Optional<T>() { return Optional<T>(); }
};
static TH _;

Ну и использовать примерно так:
template <class T>
std::ostream& operator<<(std::ostream& out, const Optional<T>& opt) {
    if (opt.IsSet())
        out << opt.Get();
    else
        out << "{Not set}";
    return out;
}

void foo(Optional<size_t*> a, Optional<float*> b, Optional<double*> c, Optional<int> d) {
    std::cout << "foo(" << a << ", " << b << ", " << c << ", " << d << ")" << std::endl;

    if (a.IsSet())
        *a = 10;
}

int main() {
    size_t a;
    float b;
    double c;
    int d = 42;
    foo(&a, &b, &c, d);
    foo(&a, &b, &c, _);
    foo(_, _, _, d);
    foo(_, _, _, _);
    return 0;
}
Re: Как избавиться от лишних аргументов
От: watch-maker  
Дата: 25.09.12 13:05
Оценка:
Здравствуйте, PlusMyTwitterFace, Вы писали:

PMT>- Создать в file_scope переменные всех фундаментальных типов с приставкой "dummy", которые, собственно, и будут использоваться для передачи в такие функции. Также не самый лучший вариант — лишнее засорение глобального пространства имён. Кроме того, в подобные функции иногда передаются даже User-defined types, а писать dummy-версии для каждого типа, используемого в программе — это какой-то ужас.


Dummy заглушки можно почти автоматически создавать:
struct DummyPtrFactory {
    template <typename T>
    operator T*() {static T x; return &x; }
};
static DummyPtrFactory _;
    int d;
    foo(_, _, _, &d); // передаём только четвёртый аргумент

Хотя, конечно, это может привести к проблемам с многопоточностью, как и при использование любых сложных объектов у которых есть неконстистентные состояния.
Re[2]: Как избавиться от лишних аргументов
От: rg45 СССР  
Дата: 25.09.12 13:47
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Как-то так:

C>
C>template<typename T>
C>class _
C>{
C>public:
C>    operator T*() const { return v_; }
C>private:
C>    T v_;
C>};
C>…
C>int four;
C>foo(_<size_t>(), _<float>(), _<double>(), &four);
C>

C>Интересно было бы добиться синтаксиса foo(_, _, _, &four) для любого типа (с использованием шаблонного operator T*?) и при этом сохранить автоматическую деструкцию ненужного объекта.

Потоко-небезопасное решение со скрытыми статическими переменными:
struct
{
  template<typename T>
  operator T&() const
  {
    static T t;
    return t;
  }

  template<typename T>
  operator T*() const  
  { 
    return &static_cast<T&>(*this); 
  }

} const _ = {};

void foo(float*, int*, double&, long&);

int main()
{
  foo(_,_,_,_);
}
--
Справедливость выше закона. А человечность выше справедливости.
Re[2]: Как избавиться от лишних аргументов
От: Сыроежка  
Дата: 25.09.12 14:03
Оценка:
Здравствуйте, Сыроежка, Вы писали:

С>Очевидно, что вам просто нужно перегрузить функцию с нужным количеством аргументов.


Например, если у вас нет доступа к телу функции, то можно сделать так



inline void foo( int *p )
{
   std::size_t _1;
   float _2;
   double _3;

   foo(&_1, &_2, &_3, p);
}
Меня можно встретить на www.cpp.forum24.ru
Re[2]: Как избавиться от лишних аргументов
От: Erop Россия  
Дата: 25.09.12 16:38
Оценка: 1 (1) +1
Здравствуйте, watch-maker, Вы писали:

WM>static DummyPtrFactory _;[/ccode]
    int d;
WM>    foo(_, _, _, &d); // передаём только четвёртый аргумент
WM>

WM>Хотя, конечно, это может привести к проблемам с многопоточностью, как и при использование любых сложных объектов у которых есть неконстистентные состояния.


Можно совместить оба подхода, при этом довольно дёшево.
class CFakeArgument {
    typedef double alignAs_t; // можно прагамами, С++11 или ещё как замутить, не суть.
    enum { bufSize = 32 }; // В общем сколько стеку не жаль
    
    struct CBuffer {
        alignAs_t alignPad;
        char bytes[bufSize - sizeof( alignAs_t )];
    } buffer;

    typedef  void deleter_t( void* );
    deleter_t* deleter;
    void* data;
    
    CFakeArgument( const CFakeArgument& );
    void operator = ( const CFakeArgument& ); // = delete
    
    //    Полиморфная часть
    template<typename T> 
    struct CData {
        T Data;
        
        void* operator new( size_t size, CBuffer& dstBuffer )
        {
            if( sizeof( CData ) <= bufSize ) {
                assert( size <= bufSize );
                return &dstBuffer;
            } else {
                return ::new char[size];
            }
        }

        void operator delete( void* p, CBuffer& dstBuffer )
        {
            if( sizeof( CData ) <= bufSize ) {
                assert( p == 0 || p == &dstBuffer );
            } else {
                ::delete[] (char*)p;
            }
        }

        void operator delete( void* p )
        {
            if( sizeof( CData ) > bufSize ) {
                ::delete[] (char*)p;
            }
        }

        static void deleter( void* p )
        {
            delete static_cast<CData*>( p );
        }
        
        T& Save( void*& thisStorage, deleter_t*& deleterStorage )
        {
            assert( thisStorage == 0 && deleterStorage == 0 );
            thisStorage = this;
            deleterStorage = deleter;
            return Data;
        }
    };


public:
    CFakeArgument() : data( 0 ), deleter( 0 ) {};
    ~CFakeArgument() { if(deleter != 0 ) deleter( data ); }
    
    template<typename T>
    operator T&()
    {
        if( data == 0 ) {
            return ( new( buffer ) CData<T> )-> Save( data, deleter );
        } else {
            assert( deleter == CData<T>::deleter );
            return static_cast<CData<T>*>(data)->Data;
        }
    }
    template<typename T>
    operator T*() { return &(T&)(*this); }
};

#define _0_ CFakeArgument() 
/////////////////////////
//  Проверка
struct BigStruct { int Field[100]; };
void foo( int*, BigStruct*, char* ) {}
void bar() { foo( _0_, _0_, _0_ );}
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Как избавиться от лишних аргументов
От: Erop Россия  
Дата: 25.09.12 17:19
Оценка:
Здравствуйте, Сыроежка, Вы писали:

С>Например, если у вас нет доступа к телу функции, то можно сделать так


С>
С>inline void foo( int *p )
С>{
С>   std::size_t _1;
С>   float _2;
С>   double _3;

С>   foo(&_1, &_2, &_3, p);
С>}
С>


Это дело можно слегка оптимизировать... Лучше всего, конечно, было бы придумать, как сбда привернуть bind, но я пока что не придумал лучше, чем так:
template<typename T> struct TheSame { typedef T Type; }; 
template<typename TRes, typename T1, typename T2, typename T3, typename T4> 
TRes call_oooX( TRes (*f)( T1*, T2*, T3*, T4* ), typename TheSame<T4*>::Type v4 )
{
    T1 v1 = T1();
    T2 v2 = T2();
    T3 v3 = T3();
    return f( &v1, &v2, &v3, v4 );
}

//////////////////
// проверим себя
struct BigStruct { int Field[100]; };
void foo( int*, BigStruct*, char*, int* ) {}
void bar() 
{ 
    call_oooX( foo, 0 ); // и с  литералом работает!
}

если функций о-о-о-чень много, то можно написать, в принципе, набор функций ptr_bind_XoXo, ptr_bind_oXoX, и т. д. которые бурут на вход указатель на целевую функцию, а возвращают функтор, с профильтрованными полями
Типа будет как-то так:
auto foo_oXoX = ptr_bind_oXoX( foo );
foo_oXoX( arg2, arg4 )


Ну и, наконец, есть ещё один смешной вариант.
Написать такую примерно, штуку
// name1 - name4 какие-то предъобъявленные id, либо числа, либо типы, не суть важно, на самом деле
auto foo_ = call_with_optional_ptrs<name1, name2, name3, name4>( foo );
foo_( param<name2>( p2 ) & param<name4>( p4 ) );
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Как избавиться от лишних аргументов
От: PlusMyTwitterFace  
Дата: 25.09.12 18:08
Оценка:
AN>Передавай NULL, а внутри функции проверяй, если переменная != NULL, то работай с ней, а иначе — ничего не делай.

Функции принадлежат библиотеке, которую, разумеется, писал не я.
Re[2]: Как избавиться от лишних аргументов
От: PlusMyTwitterFace  
Дата: 25.09.12 18:09
Оценка:
К>Если функция принимает указатель, то она, вроде бы, обязана проверять на ноль

Не хотелось бы на это просто рассчитывать.

К>Разве только это библиотечный код, который нельзя менять.


Именно так и есть.
Re[2]: Как избавиться от лишних аргументов
От: PlusMyTwitterFace  
Дата: 25.09.12 18:13
Оценка:
M>Ну сделай несколько врапперов. Делов-то.

Как я уже сказал выше, данные функции принадлежат библиотеке.

M>и?


А ничего, что таких врапперов мне придётся тогда написать, скажем, 200?
Re[3]: Как избавиться от лишних аргументов
От: Erop Россия  
Дата: 25.09.12 18:36
Оценка:
Здравствуйте, PlusMyTwitterFace, Вы писали:

PMT>А ничего, что таких врапперов мне придётся тогда написать, скажем, 200?


Это дело можно автоматизировать, вообще-то.

У тебя сколько функций и сколько "паттернов" пропуска аргументов?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Как избавиться от лишних аргументов
От: freestyle  
Дата: 25.09.12 19:00
Оценка: 3 (1) +1
Здравствуйте, PlusMyTwitterFace, Вы писали:

AN>>Передавай NULL, а внутри функции проверяй, если переменная != NULL, то работай с ней, а иначе — ничего не делай.


PMT>Функции принадлежат библиотеке, которую, разумеется, писал не я.


Делаешь функцию-обвертку над библиотечной и все дела!


void my_foo(size_t *a, float *b, double *c, int *d)
{
    size_t dummyA;
    float dummyB;
    double dummyC;
    int dummyD;

    if (NULL == a)
        a = &dummyA;
    if (NULL == b)
        b = &dummyB;
    if (NULL == c)
        c = &dummyC;
    if (NULL == d)
        d = &dummyD;

    foo(a, b, c, d);
}
Re[2]: Как избавиться от лишних аргументов
От: freestyle  
Дата: 25.09.12 19:07
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, PlusMyTwitterFace, Вы писали:


PMT>>Можно ли каким-то образом "избавиться" от "лишних" аргументов функции?


PMT>>Например, есть у меня, допустим, функция со следующим объявлением:


PMT>>
PMT>>void foo(std::size_t* _1, float* _2, double* _3, int* _4);
PMT>>


PMT>>Что посоветуете сделать?



PD>
PD>struct PARAM
PD>{
PD>std::size_t _1;
PD>float* _2;
PD>double* _3;
PD>int* _4;
PD>};

PD>void foo(PARAM* param);

PD>// вызов

PD>PARAM param;
PD>memset(&param, 0, sizeof(PARAM);
PD>// установи нужные поля. Остальные будут NULL.
PD>foo(&param);

PD>


NULL не всегда равен 0! Ваш код не переносим.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.