C & PHP: Storing settings in an integer using bitwise operators?

后端 未结 3 781
醉梦人生
醉梦人生 2020-12-14 09:57

I\'m not familiar with bitwise operators, but I have seem them used to store simple settings before.

I need to pass several on/off options to a function, and I\'d li

相关标签:
3条回答
  • 2020-12-14 10:04

    You sure can do it in PHP.

    Let's say you have four booleans you want to store in a single value. That means we need four bits of storage space

    0000
    

    Each bit, when set individually, has a unique representation in decimal

    0001 = 1 // or 2^0
    0010 = 2 // or 2^1
    0100 = 4 // or 2^2
    1000 = 8 // or 2^3
    

    A common way to implement this is with bit masks to represent each option. PHP's error levels are done this way, for example.

    define( 'OPT_1', 1 );
    define( 'OPT_2', 2 );
    define( 'OPT_3', 4 );
    define( 'OPT_4', 8 );
    

    Then when you have an integer that represents 0 or more of these flags, you check with with the bitwise and operator which is &

    $options = bindec( '0101' );
    // can also be set like this
    // $options = OPT_1 | OPT_3;
    
    if ( $options & OPT_3 )
    {
      // option 3 is enabled
    }
    

    This operator works as such: only bits that are set in both operands are set in the result

    0101 // our options
    0100 // the value of OPT_3
    ----
    0100 // The decimal integer 4 evaluates as "true" in an expression
    

    If we checked it against OPT_2, then the result would look like this

    0101 // our options
    0010 // the value of OPT_2
    ----
    0000 // The decimal integer 0 evaluates as "false" in an expression
    
    0 讨论(0)
  • 2020-12-14 10:24

    quote "the idea is not good, really. you would better pass few boolean. if you want use bitwise then

    function someFunc($options)
    {
    
       if ($options & 1 != 0)
          //then option 1 enabled
       if ($options & (1 << 1) != 0)
          //then option 2 enabled      
       if ($options & (1 << 2) != 0)
          //then option 3 enabled      
    }
    

    "

    What you have done would be okay if you were checking for a single value, although not optimal, so checking that a bit is enabled, but lets say we wanted to be able to match any, or exact we could have the following methods

    function matchExact($in, $match) { // meets your criterion, as would a switch, case, but ultimately not suited for use with flags
        return $in === $match;
    }
    
    function matchAny($in, $match) { // meets original criterion with more lexical name however it returns true if any of the flags are true
        return $in |= $match;
    }
    

    if you then wanted to expand upon this by having specific actions only happening if bit x,y,z was enabled then you could use the following

    function matchHas($in, $match) { // more bitwise than === as allows you to conditionally branch upon specific bits being set
        return $in &= $match;
    }
    

    I also think if you are doing what was done in the above quote, flags may not be the best idea, exact values might be better, which does have the benefit of allowing more discreet actions. (0-255) for 8-bit over 8 distinct flags

    The whole reason flags work so well is because in base 2 "8" does not contain "4", and "2" does not contain "1".

     ________________________
     |8|4|2|1|Base 10 Value |
     ------------------------
     |1|1|1|1|15            |
     |1|1|1|0|14            |
     |1|1|0|1|13            |
     |1|1|0|0|12            |
     |1|0|1|1|11            |
     |1|0|1|0|10            |
     |1|0|0|1|9             |
     |1|0|0|0|8             |
     |0|1|1|1|7             |
     |0|1|1|0|6             |
     |0|1|0|1|5             |
     |0|1|0|0|4             |
     |0|0|1|1|3             |
     |0|0|1|0|2             |
     |0|0|0|1|1             |
     |0|0|0|0|0             |
     ------------------------
    
    0 讨论(0)
  • 2020-12-14 10:26

    It works pretty much the same way in both languages, a side by side comparison:

    C:

    #include <stdio.h>
    #include <stdint.h>
    
    #define FLAG_ONE 0x0001
    #define FLAG_TWO 0x0002
    #define FLAG_THREE 0x0004
    #define FLAG_FOUR 0x0008
    #define FLAG_ALL (FLAG_ONE|FLAG_TWO|FLAG_THREE|FLAG_FOUR)
    
    void make_waffles(void)
    {
       printf("Yummy! We Love Waffles!!!\n");
    }
    
    void do_something(uint32_t flags)
    {
        if (flags & FLAG_TWO)
             make_waffles();
    }
    
    int main(void)
    {
        uint32_t flags;
    
        flags |= FLAG_ALL;
    
        /* Lets make some waffles! */
        do_something(flags);
    
        return 0;
    }
    

    PHP:

    <?php
    
    define("FLAG_ONE", 0x0001);
    define("FLAG_TWO", 0x0002);
    define("FLAG_THREE", 0x0004);
    define("FLAG_FOUR", 0x0008);
    define("FLAG_ALL", FLAG_ONE|FLAG_TWO|FLAG_THREE|FLAG_FOUR);
    
    function make_waffles()
    {
        echo 'Yummy! We Love Waffles!!!';
    }
    
    function do_something($flags)
    {
        if ($flags & FLAG_TWO)
           make_waffles();
    }
    
    $flags |= FLAG_TWO;
    do_something($flags);
    
    ?>
    

    Note, you don't absolutely need to use constants, I just use them out of habit. Both examples will run, I compiled the C version via gcc -Wall flags.c -o flags. Change flags in either example to anything but FLAG_TWO or FLAG_ALL and (sadly) no waffles will be made.

    In the C version, you don't have to tickle the preprocessor, it could quite easily be an enum, etc - that's an exercise for the reader.

    0 讨论(0)
提交回复
热议问题