简体   繁体   中英

Why accept void* pointer as a function argument when it is always cast to the same type in the function body in C?

I saw some C functions take a void* pointer as an argument, then in the body of the function, the only usage of the pointer is done after casting it to the same type each time it is used ( unsigned char* in my case).

I know this is a legal operation, my question is why not just accept a pointer to unsigned char instead of accepting a void* and casting it each time? Is there some performance improvement or some limitation on doing this?

If you want a code example, have a look at profileImage method (line 933) at the parameter datum (and the cast is at line 989):

MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
   const void *datum,const size_t length,ExceptionInfo *exception)
 {
 #define ProfileImageTag  "Profile/Image"
 #ifndef TYPE_XYZ_8
   #define TYPE_XYZ_8 (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(1))
 #endif
 #define ThrowProfileException(severity,tag,context) \
 { \
   if (profile != (StringInfo *) NULL) \
      profile=DestroyStringInfo(profile); \
   if (cms_context != (cmsContext) NULL) \
     cmsDeleteContext(cms_context); \
   if (source_info.profile != (cmsHPROFILE) NULL) \
     (void) cmsCloseProfile(source_info.profile); \
   if (target_info.profile != (cmsHPROFILE) NULL) \
     (void) cmsCloseProfile(target_info.profile); \
   ThrowBinaryException(severity,tag,context); \
 }
  
   MagickBooleanType
     status;
  
   StringInfo
     *profile;
  
   assert(image != (Image *) NULL);
   assert(image->signature == MagickCoreSignature);
   assert(name != (const char *) NULL);
   if (IsEventLogging() != MagickFalse)
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   if ((datum == (const void *) NULL) || (length == 0))
     {
       char
         *next;
  
       /*
         Delete image profile(s).
       */
       ResetImageProfileIterator(image);
       for (next=GetNextImageProfile(image); next != (const char *) NULL; )
       {
         if (IsOptionMember(next,name) != MagickFalse)
           {
             (void) DeleteImageProfile(image,next);
             ResetImageProfileIterator(image);
           }
         next=GetNextImageProfile(image);
       }
       return(MagickTrue);
     }
   /*
     Add a ICC, IPTC, or generic profile to the image.
   */
   status=MagickTrue;
   profile=AcquireStringInfo((size_t) length);
   SetStringInfoDatum(profile,(unsigned char *) datum);
   if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
     status=SetImageProfile(image,name,profile,exception);
   else
     {
       const StringInfo
         *icc_profile;
  
       icc_profile=GetImageProfile(image,"icc");
       if ((icc_profile != (const StringInfo *) NULL) &&
           (CompareStringInfo(icc_profile,profile) == 0))
         {
           const char
             *value;
  
           value=GetImageProperty(image,"exif:ColorSpace",exception);
           (void) value;
           if (LocaleCompare(value,"1") != 0)
             (void) SetsRGBImageProfile(image,exception);
           value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
           if (LocaleCompare(value,"R98.") != 0)
             (void) SetsRGBImageProfile(image,exception);
           icc_profile=GetImageProfile(image,"icc");
         }
       if ((icc_profile != (const StringInfo *) NULL) &&
           (CompareStringInfo(icc_profile,profile) == 0))
         {
           profile=DestroyStringInfo(profile);
           return(MagickTrue);
         }
 #if !defined(MAGICKCORE_LCMS_DELEGATE)
       (void) ThrowMagickException(exception,GetMagickModule(),
         MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
         "'%s' (LCMS)",image->filename);
 #else
       {
         cmsContext
           cms_context;
  
         CMSExceptionInfo
           cms_exception;
  
         LCMSInfo
           source_info,
           target_info;
  
         /*
           Transform pixel colors as defined by the color profiles.
         */
         cms_exception.image=image;
         cms_exception.exception=exception;
         cms_context=cmsCreateContext(NULL,&cms_exception);
         if (cms_context == (cmsContext) NULL)
           {
             profile=DestroyStringInfo(profile);
             ThrowBinaryException(ResourceLimitError,
               "ColorspaceColorProfileMismatch",name);
           }
         cmsSetLogErrorHandlerTHR(cms_context,CMSExceptionHandler);
         source_info.profile=cmsOpenProfileFromMemTHR(cms_context,
           GetStringInfoDatum(profile),(cmsUInt32Number)
           GetStringInfoLength(profile));
         if (source_info.profile == (cmsHPROFILE) NULL)
           {
             profile=DestroyStringInfo(profile);
             cmsDeleteContext(cms_context);
             ThrowBinaryException(ResourceLimitError,
               "ColorspaceColorProfileMismatch",name);
           }
         if ((cmsGetDeviceClass(source_info.profile) != cmsSigLinkClass) &&
             (icc_profile == (StringInfo *) NULL))
           status=SetImageProfile(image,name,profile,exception);
         else
           {
             CacheView
               *image_view;
  
             cmsColorSpaceSignature
               signature;
  
             cmsHTRANSFORM
               *magick_restrict transform;
  
             cmsUInt32Number
               flags;
  
             MagickBooleanType
               highres;
  
             MagickOffsetType
               progress;
  
             ssize_t
               y;
  
             target_info.profile=(cmsHPROFILE) NULL;
             if (icc_profile != (StringInfo *) NULL)
               {
                 target_info.profile=source_info.profile;
                 source_info.profile=cmsOpenProfileFromMemTHR(cms_context,
                   GetStringInfoDatum(icc_profile),(cmsUInt32Number)
                   GetStringInfoLength(icc_profile));
                 if (source_info.profile == (cmsHPROFILE) NULL)
                   ThrowProfileException(ResourceLimitError,
                     "ColorspaceColorProfileMismatch",name);
               }
             highres=MagickTrue;
 #if !defined(MAGICKCORE_HDRI_SUPPORT) || (MAGICKCORE_QUANTUM_DEPTH > 16)
             {
               const char
                 *artifact;
  
               artifact=GetImageArtifact(image,"profile:highres-transform");
               if (IsStringFalse(artifact) != MagickFalse)
                 highres=MagickFalse;
             }
 #endif
             SetLCMSInfoScale(&source_info,1.0);
             SetLCMSInfoTranslate(&source_info,0.0);
             source_info.colorspace=sRGBColorspace;
             source_info.channels=3;
             switch (cmsGetColorSpace(source_info.profile))
             {
               case cmsSigCmykData:
               {
                 source_info.colorspace=CMYKColorspace;
                 source_info.channels=4;
                 if (highres != MagickFalse)
                   {
                     source_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
                     SetLCMSInfoScale(&source_info,100.0);
                   }
 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
                 else
                   source_info.type=(cmsUInt32Number) TYPE_CMYK_8;
 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
                 else
                   source_info.type=(cmsUInt32Number) TYPE_CMYK_16;
 #endif
                 break;
               }
               case cmsSigGrayData:
               {
                 source_info.colorspace=GRAYColorspace;
                 source_info.channels=1;
                 if (highres != MagickFalse)
                   source_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
                 else
                   source_info.type=(cmsUInt32Number) TYPE_GRAY_8;
 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
                 else
                   source_info.type=(cmsUInt32Number) TYPE_GRAY_16;
 #endif
                 break;
               }
               case cmsSigLabData:
               {
                 source_info.colorspace=LabColorspace;
                 if (highres != MagickFalse)
                   {
                     source_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
                     source_info.scale[0]=100.0;
                     source_info.scale[1]=255.0;
                     source_info.scale[2]=255.0;
                     source_info.translate[1]=(-0.5);
                     source_info.translate[2]=(-0.5);
                   }
 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
                 else
                   source_info.type=(cmsUInt32Number) TYPE_Lab_8;
 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
                 else
                   source_info.type=(cmsUInt32Number) TYPE_Lab_16;
 #endif
                 break;
               }
               case cmsSigRgbData:
               {
                 source_info.colorspace=sRGBColorspace;
                 if (highres != MagickFalse)
                   source_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
                 else
                   source_info.type=(cmsUInt32Number) TYPE_RGB_8;
 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
                 else
                   source_info.type=(cmsUInt32Number) TYPE_RGB_16;
 #endif
                 break;
               }
               case cmsSigXYZData:
               {
                 source_info.colorspace=XYZColorspace;
                 if (highres != MagickFalse)
                   source_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
                 else
                   source_info.type=(cmsUInt32Number) TYPE_XYZ_8;
 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
                 else
                   source_info.type=(cmsUInt32Number) TYPE_XYZ_16;
 #endif
                 break;
               }
               default:
                 ThrowProfileException(ImageError,
                   "ColorspaceColorProfileMismatch",name);
             }
             signature=cmsGetPCS(source_info.profile);
             if (target_info.profile != (cmsHPROFILE) NULL)
               signature=cmsGetColorSpace(target_info.profile);
             SetLCMSInfoScale(&target_info,1.0);
             SetLCMSInfoTranslate(&target_info,0.0);
             target_info.channels=3;
             switch (signature)
             {
               case cmsSigCmykData:
               {
                 target_info.colorspace=CMYKColorspace;
                 target_info.channels=4;
                 if (highres != MagickFalse)
                   {
                     target_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
                     SetLCMSInfoScale(&target_info,0.01);
                   }
 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
                 else
                   target_info.type=(cmsUInt32Number) TYPE_CMYK_8;
 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
                 else
                   target_info.type=(cmsUInt32Number) TYPE_CMYK_16;
 #endif
                 break;
               }
               case cmsSigGrayData:
               {
                 target_info.colorspace=GRAYColorspace;
                 target_info.channels=1;
                 if (highres != MagickFalse)
                   target_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
                 else
                   target_info.type=(cmsUInt32Number) TYPE_GRAY_8;
 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
                 else
                   target_info.type=(cmsUInt32Number) TYPE_GRAY_16;
 #endif
                 break;
               }
               case cmsSigLabData:
               {
                 target_info.colorspace=LabColorspace;
                 if (highres != MagickFalse)
                   {
                     target_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
                     target_info.scale[0]=0.01;
                     target_info.scale[1]=1/255.0;
                     target_info.scale[2]=1/255.0;
                     target_info.translate[1]=0.5;
                     target_info.translate[2]=0.5;
                   }
 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
                 else
                   target_info.type=(cmsUInt32Number) TYPE_Lab_8;
 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
                 else
                   target_info.type=(cmsUInt32Number) TYPE_Lab_16;
 #endif
                 break;
               }
               case cmsSigRgbData:
               {
                 target_info.colorspace=sRGBColorspace;
                 if (highres != MagickFalse)
                   target_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
                 else
                   target_info.type=(cmsUInt32Number) TYPE_RGB_8;
 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
                 else
                   target_info.type=(cmsUInt32Number) TYPE_RGB_16;
 #endif
                 break;
               }
               case cmsSigXYZData:
               {
                 target_info.colorspace=XYZColorspace;
                 if (highres != MagickFalse)
                   target_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
                 else
                   target_info.type=(cmsUInt32Number) TYPE_XYZ_8;
 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
                 else
                   source_info.type=(cmsUInt32Number) TYPE_XYZ_16;
 #endif
                 break;
               }
               default:
                 ThrowProfileException(ImageError,
                   "ColorspaceColorProfileMismatch",name);
             }
             switch (image->rendering_intent)
             {
               case AbsoluteIntent:
               {
                 target_info.intent=INTENT_ABSOLUTE_COLORIMETRIC;
                 break;
               }
               case PerceptualIntent:
               {
                 target_info.intent=INTENT_PERCEPTUAL;
                 break;
               }
               case RelativeIntent:
               {
                 target_info.intent=INTENT_RELATIVE_COLORIMETRIC;
                 break;
               }
               case SaturationIntent:
               {
                 target_info.intent=INTENT_SATURATION;
                 break;
               }
               default:
               {
                 target_info.intent=INTENT_PERCEPTUAL;
                 break;
               }
             }
             flags=cmsFLAGS_HIGHRESPRECALC;
 #if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
             if (image->black_point_compensation != MagickFalse)
               flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
 #endif
             transform=AcquireTransformTLS(&source_info,&target_info,flags,
               cms_context);
             if (transform == (cmsHTRANSFORM *) NULL)
               ThrowProfileException(ImageError,"UnableToCreateColorTransform",
                 name);
             /*
               Transform image as dictated by the source & target image profiles.
             */
             source_info.pixels=AcquirePixelTLS(image->columns,
               source_info.channels,highres);
             target_info.pixels=AcquirePixelTLS(image->columns,
               target_info.channels,highres);
             if ((source_info.pixels == (void **) NULL) ||
                 (target_info.pixels == (void **) NULL))
               {
                 target_info.pixels=DestroyPixelTLS(target_info.pixels);
                 source_info.pixels=DestroyPixelTLS(source_info.pixels);
                 transform=DestroyTransformTLS(transform);
                 ThrowProfileException(ResourceLimitError,
                   "MemoryAllocationFailed",image->filename);
               }
             if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
               {
                 target_info.pixels=DestroyPixelTLS(target_info.pixels);
                 source_info.pixels=DestroyPixelTLS(source_info.pixels);
                 transform=DestroyTransformTLS(transform);
                 if (source_info.profile != (cmsHPROFILE) NULL)
                   (void) cmsCloseProfile(source_info.profile);
                 if (target_info.profile != (cmsHPROFILE) NULL)
                   (void) cmsCloseProfile(target_info.profile);
                 return(MagickFalse);
               }
             if (target_info.colorspace == CMYKColorspace)
               (void) SetImageColorspace(image,target_info.colorspace,exception);
             progress=0;
             image_view=AcquireAuthenticCacheView(image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
             #pragma omp parallel for schedule(static) shared(status) \
               magick_number_threads(image,image,image->rows,1)
 #endif
             for (y=0; y < (ssize_t) image->rows; y++)
             {
               const int
                 id = GetOpenMPThreadId();
  
               MagickBooleanType
                 sync;
  
               Quantum
                 *magick_restrict q;
  
               if (status == MagickFalse)
                 continue;
               q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
                 exception);
               if (q == (Quantum *) NULL)
                 {
                   status=MagickFalse;
                   continue;
                 }
               if (highres != MagickFalse)
                 TransformDoublePixels(id,image,&source_info,&target_info,
                   transform,q);
               else
                 TransformQuantumPixels(id,image,&source_info,&target_info,
                   transform,q);
               sync=SyncCacheViewAuthenticPixels(image_view,exception);
               if (sync == MagickFalse)
                 status=MagickFalse;
               if (image->progress_monitor != (MagickProgressMonitor) NULL)
                 {
                   MagickBooleanType
                     proceed;
  
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
                   #pragma omp atomic
 #endif
                   progress++;
                   proceed=SetImageProgress(image,ProfileImageTag,progress,
                     image->rows);
                   if (proceed == MagickFalse)
                     status=MagickFalse;
                 }
             }
             image_view=DestroyCacheView(image_view);
             (void) SetImageColorspace(image,target_info.colorspace,exception);
             switch (signature)
             {
               case cmsSigRgbData:
               {
                 image->type=image->alpha_trait == UndefinedPixelTrait ?
                   TrueColorType : TrueColorAlphaType;
                 break;
               }
               case cmsSigCmykData:
               {
                 image->type=image->alpha_trait == UndefinedPixelTrait ?
                   ColorSeparationType : ColorSeparationAlphaType;
                 break;
               }
               case cmsSigGrayData:
               {
                 image->type=image->alpha_trait == UndefinedPixelTrait ?
                   GrayscaleType : GrayscaleAlphaType;
                 break;
               }
               default:
                 break;
             }
             target_info.pixels=DestroyPixelTLS(target_info.pixels);
             source_info.pixels=DestroyPixelTLS(source_info.pixels);
             transform=DestroyTransformTLS(transform);
             if ((status != MagickFalse) &&
                 (cmsGetDeviceClass(source_info.profile) != cmsSigLinkClass))
               status=SetImageProfile(image,name,profile,exception);
             if (target_info.profile != (cmsHPROFILE) NULL)
               (void) cmsCloseProfile(target_info.profile);
           }
         (void) cmsCloseProfile(source_info.profile);
         cmsDeleteContext(cms_context);
       }
 #endif
     }
   profile=DestroyStringInfo(profile);
   return(status);
 }

In the code in question...

I think they just wrote lousy code (in this one particular case is all I'm saying; and: we all write lousy code sometimes--we learn as we go/grow). They should have used unsigned char if they want that type, and maybe unsigned char* if they want the parameter to be optional by passing in a NULL in place of a value occasionally. void* is discouraged for "generic programming" purposes unless it really is necessary, as it prevents the compiler from doing strict type checking, and can lead to more bugs. Note that in the pthread library and in my examples below, it really is necessary . Those are good uses of void* . It is good when used right and used well and when used when it should be.

How to use void* type "universal functions" to accept and return any type

When void* is used properly, it has nothing to do with speed or efficiency. Rather, it has to do with versatility .

Functions which accept void* are universal functions (I don't know if that's an "official" or "accepted term"; I'm just calling them that). They can allow you to pass functions or register functions which can do anything you like, for instance.

Here's a simple example. Imagine a generic library allows you to register your callback function it will call later:

// Define `func_t` as a function ptr whose signature looks like this:
// `void* func(void*);`
typedef void* (*func_t)(void*);

// a generic library function (not written by the user of the library)
void* register_callback(func_t user_func, void* args_to_user_func)
{
    // have this function call the function passed to it, and return what it
    // returns, for demo purposes
    return user_func(args_to_user_func);
}

Now, the user of this generic library can pass in whatever function they want that accepts and returns any type they want. Ex:

// EXAMPLE 1

// user function 1
void* print_int(void* int_ptr)
{
    int val = *(int*)int_ptr; // extract the integer from the passed-in arg
    printf("%i\n", val);

    return NULL;
}

int int_to_print = 7;
register_callback(print_int, &int_to_print);


// EXAMPLE 2

typedef struct my_struct_s
{
    int i;
    float f;
    bool b;
} my_struct_t;

// user function 2
void* print_struct(void* my_struct_ptr_in)
{
    my_struct_t *my_struct_ptr = (my_struct_t*)my_struct_ptr_in;

    printf("%i, %f, %s\n", 
        my_struct_ptr->i
        my_struct_ptr->f,
        my_struct_ptr->b ? "true" : "false");

    return NULL;
}

my_struct_t my_struct = 
{
    .i = 7,
    .f = 17.1308;
    .b = true;
};
register_callback(print_struct, &my_struct);

See the beauty of this? register_callback() is now a type of universal function which can accept a callback function which can do anything and print anything and receive any type of variables. Its possibilities are unlimited.

The pthread library requires this type of "universal function" behavior for running threads. You have to pass in a function you want the thread to run, and a void* containing whatever arguments you need that function to receive. To pass "multiple" args, pass them in via a ptr to a struct containing multiple args, as I demonstrated above.

To do this in C++ , you can do the same code as above, or you can do this template voodoo instead: my answer: How to use variadic templates (parameter packs) in C++ to pass a variadic list of arguments to a sub-function

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM