2

In the header, I have

class CSomeClass
{
    const GUID m_guid;

public:
    CSomeClass();
///...
}

And in the source file

CSomeClass::CSomeClass()
    , m_guid(
        []() {
        GUID g;
        ::CoCreateGuid(&g);
        return g;
        }()
    )
{
}

As you know Guids can be used as identifications not meant to be changed. Given the ::CocreateGuid() function provides what I want as an output parameter, instead of returning it, I cannot use directly a simple call to the function for initializing the m_guid member field, that is constant.

So, a consequence of its constness, is that it must be initialized before the opening bracket in initializer list, and therefore not be simply assigned with a call to ::CocreateGuid() in the constructor body.

Is there a simpler way to initialize it than this lambda expression?

sergiol
  • 4,122
  • 4
  • 47
  • 81
  • @IgorTandetnik: Did you think I did not try what you said? Try and see. – sergiol Jun 26 '15 at 16:56
  • My initial reaction is "good grief". Just don't do it. Initialise it in the constructor, not the initializer list. Do yourself or whomever has to read and maintain your code a favour. – Robinson Jun 26 '15 at 16:58
  • 2
    You may still write in a real utility function. – Jarod42 Jun 26 '15 at 17:00
  • 1
    Right. Never mind. `const` already kicks in in the constructor body. I'm with @Jarod42 - write `private: static GUID CSomeClass::MakeGUID()` and use that to initialize `m_guid` – Igor Tandetnik Jun 26 '15 at 17:03
  • @IgorTandetnik : I think you should remove or update your first comment. – sergiol Jun 26 '15 at 17:09
  • @Robinson: The same I said to IgorTandetnik in my fist comment, i say it again to you. – sergiol Jun 26 '15 at 17:12
  • 2
    I'm also with @Jarod42 and @IgorTandetnik on having a utility function. However, instead of a `private static` member function I would completely hide it in the source file (.cpp). Make it either `static` in the .cpp or put it in an anonymous `namespace` also in the .cpp. – Cassio Neri Jun 26 '15 at 17:17
  • @CassioNeri Why static? Because it would be only accessible on its compilation unit? – sergiol Jun 26 '15 at 17:18
  • @sergiol Yes, that's the reason. – Cassio Neri Jun 26 '15 at 17:22

5 Answers5

2

When the lambda expression is correct, I would use a helper function for that:

GUID create_guid()
{
    GUID g;
    ::CoCreateGuid(&g);
    return g;
}

CSomeClass::CSomeClass() : m_guid(create_guid()) {}

In addition, create_guid() has a meaning by itself and could be reused (even if making it a implementation detail is possible/correct).

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • I ended up creating a global function like this, because it was most adequate for reuse purposes. – sergiol Jul 02 '15 at 11:34
1

You should consider wrapping the GUID in its own class:

class CGUID
{
public:
    CGUID()
    {
        CoCreateGuid(m_guid);
    }

    const GUID& guid() const { return m_guid; }
    // Maybe some useful functions:
    bool operator==(const CGUID&) const;

private:
    GUID m_guid;
};

Now you can use the above as a member:

class CSomeClass
{
    const CGUID m_guid;
...
quamrana
  • 37,849
  • 12
  • 53
  • 71
0

Your proposal is the simplest way to initialize the constant instance member.

Don't get scared of lambdas, as a matter of fact, in general it is a new style recommendation to use lambdas for complex initializations of constants and references because they share the property of only being initialized at the point of declaration (or instance member initialization in the initializer list).

Furthermore, your code triggers the "named return value optimization" and there is no copy construction at the return from the lambda.

The interface to CoCreateGuid is deficient because it requires an output argument.

If you insist on not using the lambda, I think the next most practical alternative is to, in the constructor body, de-constify using const_cast to pass it to CoCreateGuid.

Mind you that one you enter the body of a constructor the language considers all individual members to have been properly initialized, and will invoke destructors for them should an exception happens, this makes a very big difference whether something is initialized in the initializer list or left with a binary pattern of garbage.

Finally, unfortunately you can't just call CoCreateGuid with a de-constified reference to m_guid in the lambda, because the lambda will still return a value and that will overwrite the member. It is essentially the same as what you already wrote (with the exception of the default constructor of g)

TheCppZoo
  • 1,219
  • 7
  • 12
  • 2
    Regarding your suggestion "de-constify using const_cast" might work but this actually yields undefined behaviour as per 7.6.1/4 : "any attempt to modify a const object during its lifetime (3.8) results in undefined behavior." – Cassio Neri Jun 26 '15 at 17:28
  • @CassioNeri you are correct, I just want to emphasize that I am not advocating the de-constification, I am just saying that if the code does not want to initialize the constant in the only place where it is legal to do so, the most practical illegal way is as indicated – TheCppZoo Jun 26 '15 at 17:47
  • Just because a function gives its results as output parameters, it does not mean its interface is broken. In this case specifically, it is a COM call (normally they begin with ::Co and third letter is upper case), and all them follow the convention of returning a HRESULT value, for who uses them to know about how the operation went. Imagine you needed a function returning 3 different entities. You could not return them via normal return, because it can only return one. But output parameters you can have as much as you want. – sergiol Jun 26 '15 at 22:15
  • 2
    @serg tuple disagrees. – Yakk - Adam Nevraumont Jun 26 '15 at 22:51
  • I have not experience in using std::tuple, but when I will have an opportunity, I think I will try it. – sergiol Jun 26 '15 at 23:01
  • @sergiol the Co* interfaces are doubly broken: they return an error code, management of errors through exceptions is much superior. They unnecessarily produce values as output parameters. Yes, I know, those are plain C legacy routines. When interfacing with plain C, in general wrap the C APIs as soon as you can, otherwise you quickly lose C++ capabilities. – TheCppZoo Jun 26 '15 at 23:33
  • @EdMaster You seem to be very formatted by Java thinking ... I have been working on C++ for years and rarely use exceptions! – sergiol Jun 27 '15 at 00:00
  • @Yakk a function returning a tuple does not return three different entities. In this case, it would return one single entity with 3 sub-entries. – sergiol Jun 27 '15 at 00:03
  • @sergiol tomato, tomato. I'll admit naming it at the other end is a bit annoying, but less so than "return arg by pointer". Return by pointer is more often a leftover C-ism. – Yakk - Adam Nevraumont Jun 27 '15 at 00:36
  • @sergiol I am rarely accused of being positive about Java! However, manage errors through exceptions, it is vastly superior to error codes, that's one advantage of C++ over C – TheCppZoo Jun 27 '15 at 01:52
  • @Yakk : What do you mean with tomato, tomato? Maybe is is a metaphor in English language, not understandable to me. Btw, I am in Portugal and in Portuguese tomato is a slang word for testicle the same way nut is in English. – sergiol Jun 29 '15 at 18:13
  • The problem with tuple is that you can't name its sub-entries and you will have always to go there by numeric index. – sergiol Sep 24 '15 at 10:26
0

Here we abstract your pattern:

template<class A>
A co_make( HRESULT(*f)(A*) {
  A a;
  HRESULT hr = f(&a);
  Assert(SUCCEEDED(hr));
  if (!SUCCEEDED(hr))
    throw hr;
  return a;
}

CSomeClass::CSomeClass()
  m_guid(
    co_make(&::CoCreateGuid)
  )
{}

where we detect failure and assert then throw if that is the case.

I'm not sure this is simpler.

Really, write a GUID make_guid() function, stick it in some header, and call it.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
-1

It would be simpler if you declare m_guid as a mutable instance member as opposed to const. The difference is that mutable are like a const for users of a class but a perfectly fine lvalue within the class

TheCppZoo
  • 1,219
  • 7
  • 12
  • m_guid is not supposed to be changed after the CSomeClass creation, even in the CSomeClass itself. So I am not seeing any advantages coming from your approach. – sergiol Jun 26 '15 at 21:43
  • @sergiol Whether m_guid is never changed is part of the invariant of CSomeClass, the members of CSomeClass are the ones that maintain the invariant, hence, it is perfectly legitimate to initialize it in the body of the constructor m_guid and not change it ever again. The rest of the world will see a constant. – TheCppZoo Jun 30 '15 at 01:31