Link to home
Start Free TrialLog in
Avatar of MakeItWork614
MakeItWork614

asked on

How to limit type at compile time

xxxxxxxxxxxxxx
Avatar of TheComputerWhisperer
TheComputerWhisperer

Yes.

Perhaps this question should be a bit more... wordy.
Avatar of MakeItWork614

ASKER


I have a class that I'm trying to put together which I going to use to store information to a file.
Basically I'm trying to create a generic Persistent Memory class that will allow me to store the value of any POD object.

class PersistentMemory
{
public:
virtual ~PersistentMemory(){}
static const char* GetFileName();
};

template<class T>
class WritePersistentMemory : public PersistentMemory
{
public:
WritePersistentMemory (T& Src, int ID);
};

Now I want to make it so that only POD types can be used with this class, because it wouldn't make any since to use it with complex types, or with pointers, because those types of data would not be valid on a new session.

Is there any way I can make it so that my class can only compile with POD types that are not pointers.

I'm looking for a method that will work at compile time VS run time.  So I'm not looking for an RTTI method.

I was given this method to use in a previous question
    ~foo()
    {
         static const struct
         {
              union only_pod_objects_are_compatible_with_this_template_class
              {
                   char c;
                   T t; // invalid if T is non-POD
              };
         }pod_only_instance;
    }
The problem with this method, is that it still allows pointers to be compiled.

My compiler is VC++ 6.0 and GNU 3.2

Any good tips would also be appriciated.
>>Perhaps this question should be a bit more... wordy.
I was trying to edit my question, and it was taking it, so I just put some 'xxxxxxxxx' in it to see if it would take.
It did take, but when I tried again, you already added a comment, which then locked the question from being edit.

Thanks :-)
yes, use boost type_traits.  there is a is_pod meta-function which does this.  see,

  http://www.boost.org/libs/type_traits/index.htm

-- ba
Sorry burcarpat, but that method works at runtime.

I want a method that will work at compile time, so that if the wrong type is used with my class, it will not compile.
SOLUTION
Avatar of ivec
ivec

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
p.s.: using is_same will actually allow you have more control on what your class can accept, thus i would suggest using that ( i.e. you can avoid pointers, etc. very easily and refine the selection much better, without the mercy of your compiler )

-- ba
>>huh?!?  no.  boost type_traits is a compile time mechanism; i know it for a fact

I don't think you understand my question.

Could you show me code using boost that will stop non-POD types from compiling with my class?

Please show me an example.
ivec
>>Here's one way to test at compile type if the value passed to a function is of a pointer type:

How would your method STOP a pointer type from being compile at compile time?
i didn't realize there was a question in your first comment

anyway, basically, put something like,

    BOOST_STATIC_ASSERT( boost::is_pod<T>::value );

in the very first line of all of your public constructors ( or in a separate private part of your class; see the example on the boost page ).  i am assuming T is your template parameter.  this will stop the compile if T is not pod.  you can use is_same the same way: just check for boost::is_same<T,int>::value, for example for ints, again with BOOST_STATIC_ASSERT.  using boost::type_traits, you can also control the pointer types ( via boost::is_pointer + BOOST_STATIC_ASSERT )

p.s.: btw, static assert uses a nice little template trick that stops the compilation.  may be that's what you are really after.  check their page out

-- ba
Avatar of Axter
>>How would your method STOP a pointer type from being compile at compile time?

There is a way that you can use ivec's method to stop at link time.
If you leave the const void* overloaded function undefined, and define the (...) function, then when the compiler tries to link the code, it will fail when your class is used with a pointer.

Example:
void CompileWithPrimitiveOnlyHelpr(const void*);
void CompileWithPrimitiveOnlyHelpr(...){}
With VC++ 6.0 you can get a compile time error when pointers are use with the following set of functions:

template<typename T>
void CompileWithPrimitiveOnlyHelpr(T&){}
template<typename T>
void CompileWithPrimitiveOnlyHelpr(const T*);
template<typename T>
void CompileWithPrimitiveOnlyHelpr(T*);

template<typename T>
void CompileWithPrimitiveOnly(T type)
{
      CompileWithPrimitiveOnlyHelpr(type);
    static const struct
    {
        union  only_pod_objects_are_compatible_with_this_template_class
        {
            char c;
            T t; // invalid if T is non-POD
        };
    }pod_only_instance;
}
On the GNU compiler, you should also be able to get a compile time error with the following modified version of the helper functions:
template<typename T>
void CompileWithPrimitiveOnlyHelpr(T&){}
template<typename T>
void CompileWithPrimitiveOnlyHelpr(const T*){1 = 1;}  //Error will be produced only if this template function is used
template<typename T>
void CompileWithPrimitiveOnlyHelpr(T*){1 = 1;}  //Error will be produced only if this template function is used

burcarpat,
I tried looking at the boost code, to see if I can pull out the part I need, but I can't figure out their code.
I don't want to have to include the entire boost library just to get this functionallity, I don't want to have to force every developer who uses my code to also have to use the boost library.

If you can pull out the code, and/or post a method that would just let me use a single header, I would gladly give you the points if it works.
Axter,
The CompileWithPrimitiveOnly function you posted works on VC++ 6.0 but it doesn't work with the GNU compiler.
In the GNU compiler, non-POD types still compile with this function.

It does however take care of the pointer problem on both compilers.

But if the pointer is a member, then it still compiles on both compilers.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>The CompileWithPrimitiveOnly function you posted works on VC++ 6.0 but it doesn't work with the GNU compiler.

The reason the first posted code did not work with the GNU compiler, is because the GNU compiler is optimizing away the code.

By adding the IF condition statement, it forces the compiler to evaluate the union, and therefore give an error when used with a non-POD object.

the files you need are located under boost directory and they are,

  config.hpp and everything under the config directory ( config makes sure that the code is portable )
  static_assert.hpp
  type_traits.hpp and everything under the type_traits directory

the way you'll implement the thing is something like this,

  #include <boost/static_assert.hpp>
  #include <boost/type_traits.hpp>

  // your stuff

  template <typename T>
  class myclass {
  private:
     // to use is_pod
     BOOST_STATIC_ASSERT( boost::is_pod<T>::value );
     // to have more control over the types
     BOOST_STATIC_ASSERT( boost::is_same<T, int>::value );
     BOOST_STATIC_ASSERT( boost::is_same<T, float>::value );
     // etc.
     // or to test against pointers, for example
     BOOST_STATIC_ASSERT( boost::is_pointer<T >::value );
     // type_traits has a lot more meta-functions which will
     // allow you to have a very refined control.  check their
     // documentation out
  public:
    // your stuff
  }; // myclass


> "I don't want to have to force every developer who uses my code to also have to use the boost library."

actually, boost is an extremely good library.  here is a little snippet that i generally put under my messages when i suggest usage of boost:

[ about boost.org :: boost.org is an organization supported by many c++ standards committee members and provides 100% free, peer-reviewed, cross-platform libraries.  many of the boost libraries, such as their smart pointer library, are expected to end up in the next revision of the c++ standard ]

so, every developer should actually make it common practice to use boost as much as possible but that's of course purely imho

-- ba
burcarpat,
I downloaded boost, and tried copying the two hpp files into my project, and then tried to compile you code.
I got a lot of compile errors, and when I looked at the *.hpp files one had about two dozen dependancies on other *.hpp files.

I've tried to use boost in the pass, and this is one of the reasons why I gave up on it.  There are too many file dependancies.  It's hard to use any specific feature in boost without having to include dozens or hundreds of boost files.


Thanks for the effort, but I can't use the boost method if it forces me to require the boost library.  That's not a burden I want to put on my users.
Axter,
The last code you posted does work on both compilers now.
However, it still allows member pointers to compile.

Is there any way to stop objects with member data pointers from compiling?
burcarpat,
Two questions:
1. Does boost have a common define that I can use in my code to determine if the developer is using boost or not?
2. Using boost, does the is_pod detect if the object has a member pointer?

I'm thinking that if you're boost method can work to stop object with pointers from compiling, that I can then use a combination of your method and Axter's method.
I can use an #ifdef BOOST and then put code using boost to detect member pointers.

This would give me the added feature if the developer is using boost, but not force the developer to use boost in order to use my code.


burcarpat,
I just tried the is_pod with the full boost library, and it doesn't give me the right results.

It returns false for structs and classes that are POD.

class A
{
 int x;
 long y;
};

Class A is a POD, but the is_pod returns false.
If you read the first link that burcarpat posted, you'll see the following info about is_pod:
************************************
Without some (as yet unspecified) help from the compiler, is_pod will never report that a class or struct is a POD; this is always safe, if possibly sub-optimal.
If the compiler does not support partial-specialization of class templates, then this template can not be used with function types.
************************************

So this will give you the wrong results for a POD class or a POD struct.
if you read the documentation of is_pod, this situation is explained by saying, without direct support from compiler, is_pod will always pick the safe choice and assume classes and structs are always non-pod.  this is why i suggested using is_same instead as that would allow you to have better results.  i can say one thing, if boost could not come up with a fully working version of is_pod in a portable fashion, then that probably means it is impossible to do that with the current compilers

now, back to the question.  i am starting to have a better understanding of what you want.  you have an object serialization class that works without intruding the class hierarchy.  you want to limit the types of possible classes that this class can use.  object serialization is a complex topic and there is no easy solution to it.  i remember there was a boost proposal for a very complex serialization library and it finally got rejected due to several issues

to be honest, i don't know any method that allows one to look into the class and determine if all member variables are pod, not a pointer, etc.  this will become a trouble especially with private members.  i don't want to say it is not possible but i doubt it  

personally, i would take another route.  make the user explicitly register the class to be serialized.  then, allow the PersistentMemory class to be used only with registered classes.  this can be achieved by doing something like this:

--- cut from here ---
template <class T>
struct is_persistent_t {
  enum { value = false };
}; // is_persistent_t

#define REGISTER_PERSISTENT_CLASS(CLASS_NAME)\
template <>\
struct is_persistent_t<CLASS_NAME> {\
  enum { value = true };\
};
/* */

class myclass1 { };
class myclass2 { };

REGISTER_PERSISTENT_CLASS(myclass1);

template <typename T>
class persistent_memory_t {
private:
  BOOST_STATIC_ASSERT( is_persistent_t<T>::value );
public:
  // your stuff
}; // persistent_memory_t

int
main(int    _argc,
     char*  _argv[]) {

  persistent_memory_t<myclass1> pm1; // this will compile
  // persistent_memory_t<myclass2> pm2; // this won't

  return 0;

}; // main
--- cut from here ---

this way, you retain the ultimate flexibility.  you'll document REGISTER_PERSISTENT_CLASS by saying "persistence only works with pod types" and if the user misuses it, then it's their problem, you warned them

you can actually go one step further and put a bunch of boost::type_traits checks during the registration to warn the user.  heck, if you combine type_traits with boost's mpl or constructs from loki to do compile time if statements, then you can actually control a lot of stuff

finally, you might want to consider an intrusive serialization scheme.  The best way to do this is to define something like a persistent_t abstract class and force the user to inherit from that and define load() and save() methods

-- ba
>>i don't know any method that allows one to look into the class and determine if all member variables are pod,

The method I posted, can determine if all the members are POD.  The function will not compile on a class or struct that contains non-POD members.
This method is portable, and will work on any C++ compliant compiler.

What it can not do, is determine if the members are pointers.  A pointer is considered a POD type, and therefore the function will compile if the class or structure has a pointer.

There's no portable method that I'm aware of that can determine if the members are non-pointers.
> "The method I posted, can determine if all the members are POD.  The function will not compile on a class or struct that contains non-POD members."

yes, i am aware of that, that's why the full sentence was "... i don't know any method that allows one to look into the class and determine if all member variables are pod, not a pointer, etc."

checking for whether a member is a pointer or not should basically require the knowledge of all members by the external class.  that way, one can check it one by one.  yet, without that kind of info, i am guessing it can't be done

-- ba
Thanks for every one's help.

Since Axter's method came closes that what I need, I've accepted his comment as the answer.

If anyone finds a method that can determine if data members are pointers, I would glady give additional points.