Trait which checks whether class has typedef (private or other) or not
Asked Answered
R

2

7

Is there a way to check if class has a typedef which works even for private typedef?

Following code works in VS2013, but fails on ideone's gcc

template<typename T>
struct to_void
{
    typedef void type;
};

class Foo
{
    typedef int TD;
};

template <typename T, typename dummy = void>
struct has_TD : std::false_type {};

template <typename T>
struct has_TD<T, typename to_void<typename T::TD>::type > : std::true_type{};

int main()
{
    std::cout << std::boolalpha << has_TD<Foo>::value << std::endl;
}

edit - why I want this

I have custom serialization system, which can serialize arbitrary type. It has several overloads when it must behave differently (for example string). For the rest of the types, it simply writes the value in the memory. If I have composed type, I can sometimes just write into memory as well (save & load happens on the same architecture, compiled with the same compiler, so paddings will be the same, etc.). This method is valid for example for POD types (std::is_pod trait), but all POD types is only a subset of all types, supporting this serialization.

So I basically have templated function write<T> which just write sizeof(T) bytes (raw-serialization)... But I don't want this to be called by mistake, I want user, to explicitly say in their class: "this class/struct can be raw-serialized"). The way I do it is a macro ALLOW_RAW_SERIALIZE which defines some typedef which can be checked via trait. If class MyClass doesn't contains typedef, calling write(myClassInstance) will produce compiler error.

The things which which basically decide if class can be raw-serialized are its members (without reflection, members cannot be enumerated and checked automatically, so user have to provide such information). typical class looks like this:

class
  public
    ctor-dtor
    methods
  private
    methods
    members

and I want users to allow write ALLOW_RAW_SERIALIZE as close to the members as possible, so when they change some members there is a lesser chance to forgot about updating ALLOW_RAW_SERIALIZE (remove it. when it's no longer valid)

So that is why I want to check a private typedef

Since it's substitute for reflection and takes whole type and write it, I don't fell about it like breaking encapsulation or so...

Rogue answered 4/1, 2016 at 7:39 Comment(5)
This doesn't seem possible. VS behaviour looks like a bug.Turnstone
Just curious: what would be the point of knowing there's a typedef you can't use? If it were possible, it would break encapsulation in that changes to private implementation details affect client code despite the public API being stable.Anhedral
@TonyD please, see my editRogue
If ALLOW_RAW_SERIALIZE is already a macro, then surely you can extend that macro to simply make your serialisation checker a friend?Hutton
I've tried that, but failed: ideone.com/c5OyQu and ideone.com/fuqwWs maybe I've just got the syntax wrong, but if this works, it would be the perfect solutionRogue
U
1

UPDATE:

Okay, did a little research.

FYI, the [probable] reason that ideone didn't compile is that what you're doing needs -std=c++11 [or higher]. I got similar errors before adding that. But, I had to use clang++ as g++ still had problems compiling if TD was private.

But, I'm not sure this works as the only combo that printed true was if TD was public. All others of public/private and changing TD to TF produced false. Maybe VS2013 works [why?], but two other compilers have issues, either in compilation or runtime results--YMMV.

The basis for what you're doing is std::integral_constant [since c++11]. There appears to be no standard derivation from this for what you're doing. That is, from http://www.cplusplus.com/reference/type_traits/integral_constant/ the list of type traits [on the left] has nothing that matches your use case [AFAICT].

Nor does Boost.TypeTraits have anything that matches up [again, AFAICT].

From Andrei Alexandrescu's book: "Modern C++ Design: Generic Programming and Design Patterns Applied", section 2.10 Type Traits:

Usually, you will write your own trait templates and classes as your generic code needs them. Certain traits, however, are applicable to any type. They can help generic programmers to tailor template code better to the capabilities of a type.

So, it's "okay" to roll your own, if you wish.

But, even the TypeTraits he talks about [from Loki], again, doesn't have anything that matches what you're doing.

Since neither std nor Boost has anything, then the question becomes "what is standard?" [from your perspective]. There may be "fludger" c++ traits library somewhere that has an implementation, but would that be considered "standard"? YMMV

However, a question or two:

Why would one do this? What is the use for it? What about a protected typedef in a base class?

And, this seems to require knowledge of the private part of a class, and wouldn't that be a violation of either "data hiding" or encapsulation [without a friend declaration of some sort]?

So, if that last question is true, the probable [IMO] answer is that there is no standard way to do this, because it's not something one should be doing in a standard library.


Side note: This is the part that got downvoted (before I [truly] understood the question). I believe I've acquitted myself above. So, disregard the answer below.

When you use class the default visibility is private. With struct, it's public.

So, either do:

struct Foo

Or:

class Foo
{
    public:
    typedef int TD;
};

This is, of course, assuming that you want TD to be public

Upstate answered 4/1, 2016 at 7:50 Comment(5)
I don't want to specify TD as public. I am asking, whether exist a standard way to check existence of a private typedefRogue
This isn't answering the question.Barbusse
@Barbusse Got it, my bad. But, I'm looking for an answer.Upstate
when I say "standard" I don't think existing trait which do the job, but I mean custom written trait, which behaves according to the c++ standard (not exploiting compiler bugs or so). The ideone example is compiled with C++14. Please see edit of my question why I want thisRogue
No worries. That's why I quoted Andrei [about rolling your own]. But, also it won't show up in a std lib. So, you're free to implement without worrying about [not] using a std method (e.g. std::is_private). But here, g++ complains even with -std=c++17 [clang++ is happy with c++11]. But, the program does not appear to work [I did 4 versions]. Your orig returns false. I do have an idea about how to make it work the way you want. If I do, I'll post. Okay on the why [from your edit]. There may be an even easier way to do this.Upstate
A
0

If all you need is compile time checking then following code should do:

#include <iostream>

class Foo
{
    typedef int TD;
    template<typename T> friend class has_TD;
};

template <typename T>
struct has_TD
{
    typedef typename T::TD type;
};

template <typename T, typename has_TD<T>::type = 0>
void write(const T& /*data*/)
{
    std::cout << "serialize" << std::endl;
}

int main()
{
    Foo foo;
    write(foo);
}
Ajaajaccio answered 5/1, 2016 at 12:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.