tags:

views:

228

answers:

8

I perform some calculations, based on the result, I would like to either use a short int or int for some type of data for the remaining program. Can (/How can) this be done sensibly in C or C++? I don't really care about the amount of memory used (i.e., 2 or 4 bytes), my primary aim is to access generic arrays as if they contained data of this type. I would like to avoid code such as the following:

char s[128];
if (result of preliminary calculations was A)
  *((int*) s) = 50;
else
  *((short int*) s) = 50;

to set the first 4 or 2 bytes of s. A conditional global typedef would be ideal:

if (result of preliminary calculations was A)
  typedef int mytype;
else 
  typedef short int mytype;

I am not that familiar with C++ class templates (yet). Do they apply to my problem? Would I have to change the declarations throughout my program (to myclass< > and myclass< >*)?

Many thanks!

Frank

Edit: The values may not always be aligned. I.e, a int can start at position 21. Thanks for the answers.

+2  A: 

In C and C++, all type information is defined at Compile-time. So no, you cannot do this.

Smashery
We need to know what form the 'preliminary calculation' takes. If it can be done at compile time, then the entire method is feasible.
Aaron McDaid
+3  A: 

For plain C, you could do this using function pointers:

static union { s_int[32]; s_short[64]; s_char[128]; } s;

static void set_s_int(int i, int n)
{
    s.s_int[i] = n;
}

static int get_s_int(int i)
{
    return s.s_int[i];
}

static void set_s_short(int i, int n)
{
    s.s_short[i] = n;
}

static int get_s_short(int i)
{
    return s.s_short[i];
}

static void (*set_s)(int, int);
static int (*get_s)(int);

Set them once based on the preliminary calculations:

if (result of preliminary calculations was A)
{
    set_s = set_s_int;
    get_s = get_s_int;
}
else
{
    set_s = set_s_short;
    get_s = get_s_short;
}

Then just use the function pointers in the rest of the program:

set_s(0, 50);   /* Set entry 0 in array to 50 */

Your file writing function can directly reference s or s.s_char depending on how it works.

caf
If the downvoter could leave a comment, that would be nice.
caf
Unfortunately, my data-blocks will not always be aligned, should have mentioned that. However, the function pointer might still be a good work-around. I'll experiment with them further tomorrow... (It's quite late here). Thanks
Frank
@caf - it wasn't me, but I suspect it's because this is clearly a workaround to a [bigger problem that could most likely be solved in a better way](http://meta.stackoverflow.com/questions/56366/) (as per the question comments too).
detly
@Frank: If your data blocks are not always aligned, then you can't (portably) use simple assignment anymore - you'll have to use `memcpy()`. The general approach should still be workable, though.
caf
@detly: Nice to learn about the XY problem, but I am quite happy with the function pointer solution...
Frank
@caf: I now pass a pointer into s to the function and let it do the casting and assignment. This way, I can eliminate the repeated 'if (result of preliminary calculations was A)' testing, which is good
Frank
@Frank: Sure, that's just making the approach re-entrant, which is good. You should still use `memcpy()` to copy the `int` or `short` into the array rather than casting-and-assignment, though (misaligned access can cause a runtime fault eg on SPARC).
caf
+1  A: 

You could abuse templates for this purpose. Any code that's subject to the decision would have to be templated based on the int type. One branch would instantiate the int version, the other would instantiate the short int version. This is probably a bad idea*.

Edit

*Well, it's only a bad idea to apply this to your overall architecture. If you have a particular data type that encapsulates the varied behavior, a template should work just fine.

Here's a variation on Aaron McDaid's answer to illustrate it's use with conditions:

#include <iostream>
#include <string>
using namespace std;

template<int x> struct OddOrEven    { typedef typename OddOrEven<x-2>::t t; };
template<>      struct OddOrEven<0> { typedef short t; };
template<>      struct OddOrEven<1> { typedef int t; };

int main() {
    cout << "int or short? ";
    string which;
    cin >> which;

    if (which.compare("int") == 0)
        cout << sizeof(OddOrEven<1>::t) << endl;
    else if (which.compare("short") == 0)
        cout << sizeof(OddOrEven<0>::t) << endl;
    else
        cout << "Please answer with either int or short next time." << endl;

    return 0;
}
Cogwheel - Matthew Orlando
And you ask why I downvoted your other answer. Sheesh.
anon
It's not all that bad. I work with a solution more or less like this, for dealing with images with different Pixel sizes (or complex pixel values, like a numeric grid). A base class like Pixel, the templated class PixelImp<T> : Pixel, a set of helper classes that does comparisons, equalities between things of different sizes. Algorithms just use the base class and work regardless of pixel type.
Vitor Py
Yeah, when you can neatly encapsulate the desired behavior it would be alright, but I didn't initially get that impression from the question.
Cogwheel - Matthew Orlando
@Neil: I think I see where we're talking cross-purposes. In this case, the problem is architectural whereas the other one was a single pair of generally useful functions. I'm not harboring any grudge... I'm generally interested in the debate we were having. I don't understand why you have to continue to be so hostile. Did you see my most recent comments?
Cogwheel - Matthew Orlando
+1  A: 

I think your main problem is how you plan to read the data from s later on if you don't know what type to read.

If you have that part covered, you can use a union:

union myintegers
{
   int   ints[32];
   short shorts[64];
};

Now simply use the type you want.

myintegers s;
if (result of preliminary calculations was A)
  s.ints[0] = 50;
else
  s.shorts[0] = 50;

As a step further, you could wrap it all in a class which is constructed with result of preliminary calculations was A and overrides the operators * and [] to store in one or the other.

But are you sure you want any of that?

zvonimir
A: 

In current C++ standard (C++03), you can't.

In fact you can use some advanced metaprogramming tricks but it will not help most of the time.

In the next standard (C++0x, certainly C++11 in the end), you will be able to use the keyword decltype to get the type of an expression. If you're using VC10 (VS2010) or GCC 4.4 or more recent, then you already have the feature available.

Klaim
That isn't what he seems to want to do.
anon
I am using VS2010... I will look into a decltype example...
Frank
A: 

This is a code snippet from a project I had a while back.

   void* m_pdata;
   if (e_data_type == eU8C1){
      pimage_data = new unsigned char[size_x * size_y];
   }
   if (e_data_type == eU16C1){
      pimage_data = new unsigned short[size_x * size_y];
   }

I hope it can help you

Eric
+2  A: 

If the result of the preliminary calculations can be found at compile time, then this can work. Here are some simple examples to show how this can work. To do more complicated examples, see http://en.wikipedia.org/wiki/Template_metaprogramming

using namespace std;
#include <iostream>

template<int x> struct OddOrEven    { typedef typename OddOrEven<x-2>::t t; };
template<>      struct OddOrEven<0> { typedef short t; };
template<>      struct OddOrEven<1> { typedef int t; };

template<bool makeMeAnInt> struct X       { typedef short t; };
template<>                 struct X<true> { typedef int t;   };

int main(void) {
  cout << sizeof(X<false>::t) << endl;
  cout << sizeof(X<true>::t) << endl;
  cout << sizeof(OddOrEven<0>::t) << endl;
  cout << sizeof(OddOrEven<1>::t) << endl;
  cout << sizeof(OddOrEven<2>::t) << endl;
  cout << sizeof(OddOrEven<3>::t) << endl;
  cout << sizeof(OddOrEven<4>::t) << endl;
  cout << sizeof(OddOrEven<5>::t) << endl;
}

I think above is standard C++, but if not I can tell you this work on g++ (Debian 4.3.2-1.1) 4.3.2

Aaron McDaid
Interesting! But I cannot use it, since the preliminary calculations will involve variables, and OddOrEven< > requires a constant.
Frank
Sure you can! As long as you instantiate all of the different varieties inside your `if` statements, you'll be fine. This is what I was getting at with my answer. However, your question made it seem like your needs were broader than just one particular data type. Otherwise i wouldn't have said it was a bad idea ;)
Cogwheel - Matthew Orlando
I updated my answer with a variation on this theme to show how you can use it with an `if`.
Cogwheel - Matthew Orlando
A: 

Since your stated goal is to store information efficiently on disk, you should learn to stop writing memory images of C/C++ data structures to disk directly and instead serialize your data. Then you can use any of a number of forms of variable-length coding ("vlc") to get the effect you want. The simplest is a coding with 7 bits per byte where the 8th bit is a continuation flag indicating that the value is continued in the next byte. So 259 would be stored as (binary, with continuation bit marked by spacing and byte boundaries marked by ;):

1 0000010 ; 0 0000011

Alternatively you could use the head nibble to signal the number of bytes that will follow, or use a scheme similar to UTF-8 with slightly more overhead but stricter resynchronization guarantees. There are also vlcs with are designed to be parsable and easily resynchronized when reading either forward or in reverse.

R..