1

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);
 }
HII
  • 3,420
  • 1
  • 14
  • 35
  • 2
    Without any code we can't tell. A typical reason is that the caller doesn't know the type. Only the function being called knows the type and can do the cast. Take a look at the compare function used by `qsort` – Support Ukraine Nov 23 '22 at 06:33
  • 1
    `pthread_create` for example, requires a pointer to a function that takes a `void *` and returns a `void *`. Inside the thread function, you'll cast the argument to the proper type. – user3386109 Nov 23 '22 at 06:33
  • 2
    And for performance... I have never seen a system where there are any performance improvement involved. – Support Ukraine Nov 23 '22 at 06:35
  • @SupportUkraine have a look at [profileImage](https://imagemagick.org/api/MagickCore/profile_8c_source.html) method (line 933) at the parameter `datum` – HII Nov 23 '22 at 06:42
  • @Haidar I don't know much about image processing so I can't tell the reason in this specific case. – Support Ukraine Nov 23 '22 at 06:58
  • @SupportUkraine it is just an example, my question is in general – HII Nov 23 '22 at 06:59
  • @Haidar There are several use cases for passing void-pointers. I already mentioned the case where the caller doesn't know the exact type. In general it's related to generic programming, e.g. writing code that can "carry around" a pointer without actually knowing (nor caring) about what that pointer actually points to. As example I mentioned `qsort`. Another user has mentioned `pthread_create`. I know the principle is also used in DPDK structures to allow DPDK apps to store private data in a private format unknown to the DPDK core.... there are many, many examples – Support Ukraine Nov 23 '22 at 07:06
  • A 500 line long function is hardly a good example of anything. – n. m. could be an AI Nov 23 '22 at 07:08
  • @n.m. you can search for the word `datum`, and see that its usage is done after casting it to `unsigned char*`, there is no other casting, hope this makes my question clearer – HII Nov 23 '22 at 07:10
  • It doesn't matter. A 500 line function is a horrible thing. Don't expect its guts to be an example of good coding practices. – n. m. could be an AI Nov 23 '22 at 07:14
  • @n.m. this is exactly what my question is about, I don't want to read minds of people, I am just asking if 1. in general and 2. in this specific case, is there any advantage to use this pattern over just accepting the argument being directly the type you as you want to use it. – HII Nov 23 '22 at 07:16
  • @Haidar The `profileImage` function seems to be an API function. Maybe users of this API function may have the data (datum) stored in arrays of different types while the profileImage function can do its job by assuming it's `unsigned char` but without actually knowing the real format. I know too little about image processing to be sure... but consider a function like `memcpy`. It can copy any type of data as long as it knows the size. So it uses void-pointers as interface. That way you have a generic function instead of one function for copying int-blocks, another for copying float-blocks, etc – Support Ukraine Nov 23 '22 at 07:30
  • There are many possible valid reasons to have a `void*` parameter and then cast it to some specific type. It is hard to tell which of those reasons, if any, the author of the code you chose as an example had in mind. It is too big an unwieldy. Assume it is just bad coding or legacy API preserved for backwards compatibility. If you want something less vague, try to find an example a mere mortal doesn't need a full workweek to analyse. – n. m. could be an AI Nov 23 '22 at 07:30
  • 1
    BTW: Maybe not relevant here but just for info.... before introduction of void-pointers in C, it was common to use (unsigned) char-pointers for the same purpose, i.e. for writing generic functions – Support Ukraine Nov 23 '22 at 07:32
  • @SupportUkraine thanks for the last comment, it is most probably bcz of historical reasons as you stated, as the code is very old. idk if you should put that as an answer as I think it is the closest answer ( but its not 100% confirmed) – HII Nov 23 '22 at 09:54

1 Answers1

4

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

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
  • this is a good explanation, however, in the example I mentioned, they are not using generic functions. they accept a `void*` arg and always cast it to `unsigned char*`, please can you update your answer to cover this case as well? – HII Nov 23 '22 at 07:08
  • 1
    @Haidar, function fixed. I think they just wrote lousy code. 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. – Gabriel Staples Nov 23 '22 at 07:15
  • 1
    I hope I didn't miss something. If anyone sees something I'm overlooking about that, and I am wrong, please let me know and I'll fix it. But, meanwhile, I've added that to the top of my answer. – Gabriel Staples Nov 23 '22 at 07:17
  • "void* is discouraged for "generic programming" purposes" I'm not sure this statement applies to C. I know that in e.g. C++ other ways are preferred - for instance templates. But in C I'm not so sure - do you have any references related to C? – Support Ukraine Nov 23 '22 at 08:21
  • @SupportUkraine, I'm talking about C not C++. But remember I said: "...unless it really is necessary." And in the examples I show, and in the case of pthreads, it really is necessary. I am _not_ saying don't use it for generic programming. I'm saying: evaluate the architecture and be sure you're not just using it lazily or unnecessarily. Use it. Use it well. Use it in C++ even. I don't care. Just use it well. I don't have any references off the top of my head, but I've seen it come up over the years where sometimes it's being abused and shouldn't be. – Gabriel Staples Nov 23 '22 at 08:58
  • @SupportUkraine, see here too: https://www.quora.com/What-are-the-disadvantages-of-void-pointer-in-C-language. One case I really didn't like in my own experience was someone creating "classes" and "class-like" objects and polymorphism in C, where each object had a `void* (*func_t)(void*)` type function pointer to a function and it was never clear which function it was pointing to. It was an indexing nightmare. I'd have to jump all over looking for the assignment to see which function was actually being called and where it got assigned, & what the real types were. In my opinion: don't do that. – Gabriel Staples Nov 23 '22 at 09:04
  • @GabrielStaples OT but I seldom see https://www.quora.com/ as an expert source. Anyway... I do agree that void pointers should not be used when a typed pointer can be used. My "nitpick" was in relation to the term "generic". Writing generic functions/features in C often (nearly always) require use of void-pointers. An alternative would be macros but IMO that's even worse... – Support Ukraine Nov 23 '22 at 09:15
  • @SupportUkraine, I actually prefer macros where possible. But again, without specific code review examples and long discussions, we wouldn't get anywhere on that topic. Even in C++ macros have their virtues, despite being a hated feature by many C++ devs. Just remember: everything has a tradeoff, and rarely is there a clear and consistent "winner". Programmers are filled with bias and opinion, myself included. I just try to be informed is all and acknowledge the tradeoffs. – Gabriel Staples Nov 23 '22 at 09:20