Optional argument passed by reference



  • Hello.

    I often need to make optional function arguments passed by reference. I do it this way:

    tData *GetData(tDataSize &size = *reinterpret_cast<tDataSize *>(NULL))
    {
      tData *data;
      tDataSize dataSize;
    
      [...] //Here I calculate data and dataSize
    
      if(&size != NULL) size=dataSize;
      return data;
    };
    

    Thus, when user needs in data pointer and data size, he calls:

    tDataSize dataSize;
    tData *data(GetData(dataSize));
    

    When he does not needs in size, he just call tData *data(GetData()); .

    Is it safe doing it this way?
    How to make dataSize const in the first case of call? :

    tDataSize dataSize; //How to make this const?
    tData const *const data(GetData(dataSize));
    

  • Mod

    SAn schrieb:

    Is it safe doing it this way?

    No. It calls for undefined behaviour. References always refer to storage, a null pointer does not.

    How to make dataSize const in the first case of call? :

    tDataSize dataSize; //How to make this const?
    tData const *const data(GetData(dataSize));
    

    please rephrase.



  • But I do not use the reference until I check that it is actually !=NULL . Is this still unsafe ??

    Before I figured out the NULL-reference, I used this method, but I do not like it:

    tDataSize tempDataSize;
    
    tData *GetData(tDataSize &size = tempDataSize)
    {
      tData *data;
      tDataSize dataSize;
    
      [...] //Here I calculate data and dataSize
    
      if(&size != &tempDataSize) //Just to illustrate how to make detection of optional argument, so I can skip it calculation for speed-up
        size=dataSize;
      return data;
    };
    

    After that the argument size is optional, but I do not like that I need to store additional temporary instance of tDataSize object.



  • camper schrieb:

    How to make dataSize const in the first case of call? :

    tDataSize dataSize; //How to make this const?
    tData const *const data(GetData(dataSize));
    

    please rephrase.

    I just want dataSize to be protected from further modification after correct size of data stored in it.



  • Also I write such things:

    void DoSomething(void)
    {
      tData &data(GetData());
      if(&data==NULL) return; //GetData returned NULL
      data.a=1.0;
      data.b=...
    }
    

    I can rewrite it in more "correct" form, but like dots ( . ) and not like arrows ( -> ) :

    void DoSomething(void)
    {
      tData *data(GetData());
      if(data==NULL) return; //GetData returned NULL
      data->a=1.0;
      data->b=...
    }
    

  • Mod

    SAn schrieb:

    But I do not use the reference until I check that it is actually !=NULL . Is this still unsafe ??

    Dereferencing a null pointer is in itself undefined behaviour, whatever happens after doesn't matter. If an argument is optional, either using a dummy object as shown or a pointer parameter should be preferred. Using an overloaded function would be sufficient as well.

    SAn schrieb:

    camper schrieb:

    How to make dataSize const in the first case of call? :

    tDataSize dataSize; //How to make this const?
    tData const *const data(GetData(dataSize));
    

    please rephrase.

    I just want dataSize to be protected from further modification after correct size of data stored in it.

    There is no way to do that. Your function doesn't really (logically) take a parameter, rather it returns two values. The function should return a pair of values - not needing one of the return values is not a problem then. A reference to boost.tuple is in order.

    std::pair<tData*,tDataSize> GetData();
    
    tData* foo = GetData().first; // if we don't need size
    tDataSize bar = GetData().second; // if we don't need the pointer
    boost::tie(foo,bar) = GetData(); // if we want both
    std::pair<tData*,tDataSize> foobar = GetData(); // might be a little bit awkward
    // C++0x
    auto foobar = GetData(); // would solve that problem
    


  • SAn schrieb:

    But I do not use the reference until I check that it is actually !=NULL . Is this still unsafe ??

    As camper said before what you are doing is not safe. References always must be bound to valid objects, NULL doesn't point to any one. Use pointers to distinguish between valid and invalid objects, not references.
    But this doesn't seem to be your real problem.

    SAn schrieb:

    if(&size != &tempDataSize) //Just to illustrate how to make detection of optional argument, so I can skip it calculation for speed-up
    

    This is not good practise. Use overloaded functions if you often need optional function arguments passed by reference. Write redundant code in seperate functions. Combine this with strategy pattern or something like that.

    Sometimes a const reference may be sufficient for your needs.

    tData* GetData(const tDataSize& size = tDataSize())
    

    Remember, temporary objects can only be bound to references to const objects.

    SAn schrieb:

    I just want dataSize to be protected from further modification after correct size of data stored in it.

    This is not possible at compile time.


  • Mod

    groovemaster schrieb:

    Lie when your wife is waking. Lie when your belly's aching. Lie when you know she's faking. Lie, sell shoes, and lie.
    C-Casts in C++ stinken - basta

    They do make lying that much easier though...



  • 😃



  • How much new information for me!

    std::pair is standard thing in C++ ??
    I found it in <utility> in my system, but I do not know if it will be available on other platforms and if it will be understandable by users of my library. For example, I learned about it a few minutes ago.



  • everything in namespace std is standardized and plattform independent (mostly, in the end it depends upon how standard compatible the implementation for the platform is)


  • Mod

    SAn schrieb:

    std::pair is standard thing in C++ ??

    yes.

    Another way to deal with this particular case would be to think of the return value as a view into an array allocated elsewhere. boost has the means for that as well, Boost.MultiArray
    for example

    const_multi_array_ref<tData,1> GetData(); // a 1-dimensional array, the ref suffix denotes that it refers to storage allocated elsewhere
    
    const_multi_array_ref<tData,1> data = GetData();
    tData const* ptr = data.origin(); // if we absolutely have to have a pointer,
    // since multi_array is a proper STL sequence chances are that you won't need it
    tDataSize size = data.size();
    


  • groovemaster schrieb:

    SAn schrieb:

    if(&size != &tempDataSize) //Just to illustrate how to make detection of optional argument, so I can skip it calculation for speed-up
    

    This is not good practise. Use overloaded functions if you often need optional function arguments passed by reference. Write redundant code in seperate functions. Combine this with strategy pattern or something like that.

    What is "strategy pattern" and how to use it in this case?



  • Hi SAn,

    old thread 🙂

    The strategy pattern is a design pattern. You can find out more about design patterns here.

    The strategy pattern is useful if you have an algorithm or more general code that needs different implementations (strategy) at a certain place depending on the input or your needs. You simply abstract these implementations in an interface. It is like a kind of "macro parameter".
    I don't know if this will be really helpful for your application due to the little code you posted. But it looks a bit like. Just take it as a suggestion for better designs.


Anmelden zum Antworten